def harmonic_series(): out = Rational(1) ctr = 2 while True: yield out out += Rational(1, ctr) ctr += 1
def liouville_convergents(b=10): k = 1 out = Rational(0) ctr = 1 while True: out += Rational(1, b**k) ctr += 1 k *= ctr yield out
def farey_sequence(n): """All unique fractions between 0 and 1 with denominator less than or equal to n""" a, b, c, d = 0, 1, 1, n yield Rational(0) while c <= n: k = (n + b) // d a, b, c, d = c, d, k * c - a, k * d - b yield Rational(a, b)
def question_mark_func(n): """Use the Farey sequences to calculate the question mark function""" known_val = {Rational(0): Rational(0), Rational(1): Rational(1)} out = [(Rational(0), Rational(0))] for i in range(1, n): S = farey_sequence(i) a = next(S) b = next(S) while True: new = mediant(a, b) val = (known_val[a] + known_val[b]) / 2 if new not in known_val: known_val[new] = val out.append((new, val)) a = b try: b = next(S) except StopIteration: break out += [(Rational(1), Rational(1))] out = sort_by_nth(out, 0) return out
def bernoulli_numbers(n): B = 0 for k in range(n + 1): for v in range(k + 1): B += (-1)**v * choose(k, v) * Rational(v**n, k + 1) return B
def rational_round(Q,dlim): """Best approximation of Q with denominator of d or less, by semi-convergents""" Q = cast_to_rational(Q) if type(dlim) != int: raise TypeError("dlim must be integer") if dlim < 1: raise ZeroDivisionError # https://shreevatsa.wordpress.com/2011/01/10/not-all-best-rational-approximations-are-the-convergents-of-the-continued-fraction/ a = CFrac(Q).terms prev = Rational(a[0]) for pos,val in enumerate(a): # Try appending the floor of half the next convergent semi = a[:pos]+[(val-1)//2+1] semi = CFrac(semi) # If it is worse than the last semiconvergent add 1 if abs(semi.as_rational() - Q) > abs(prev - Q): semi[pos] += 1 while semi.terms[pos] <= val: if semi.as_rational().d > dlim: semi[pos] -= 1 return prev prev = semi.as_rational() semi[pos] += 1 return Q
def all_pos_rationals(): """Generate all positive rational numbers""" yield Rational(0) diag = 1 prev = set() while True: N = 1 D = diag for i in range(diag): r = Rational(N,D) if r not in prev: prev.add(r) yield r N += 1 D -= 1 diag += 1
def cast_to_rational(T): """Best effort to transform input to a rational number""" if type(T) == Rational: return T elif type(T) == int: return Rational(T) elif type(T) == float: return cast_to_rational(str(T)) elif type(T) == CFrac: return T.as_rational() elif type(T) == str: for func in [ ratio_to_frac, int_str_to_frac, term_dec_to_frac, rep_dec_to_frac, scientific_to_frac ]: out = func(T) if type(out) == Rational: return out raise ValueError(f"Could not cast {T} to Rational") else: raise ValueError(f"Could not cast {T} to Rational")
def rep_dec_to_frac(S): """Convert a string representing a repeating decimal to Rational""" m = re.fullmatch("-?\d*\.\d*\(\d+\)", S) is_neg = 1 if m: if S[0] == "-": is_neg = -1 S = S[1:] T = re.sub("\(|\)|\.", "", S) D = first_where(S, ".") - 1 # pos of decimal R = first_where(S, "(") - 2 # pos of repeating part A = int(T) B = int(T[:R + 1]) mul_full = 10**(len(T) - 1) mul_rep = 10**R mul_dec = 10**D return Rational(A - B, (mul_full - mul_rep)) * mul_dec * is_neg else: return None
def int_str_to_frac(S): """Convert a string representing an integer to Rational""" m = re.fullmatch("-?\d+", S) if m: return Rational(int(S)) else: return None
def harmonic_progression(a=1, d=1): a = cast_to_rational(a) d = cast_to_rational(d) den = a one = Rational(1) while True: yield one / den den += d
def convergents(self): """Rational convergents""" A1 = self.terms[0] A2 = self.terms[1] * self.terms[0] + 1 B1 = 1 B2 = self.terms[1] yield Rational(A1, B1) for d in self.terms[2:]: A1, A2 = A2, d * A2 + A1 B1, B2 = B2, d * B2 + B1 yield Rational(A1, B1) yield Rational(A2, B2)
def convergents(self): """Rational convergents""" A1 = self.dens[0] A2 = self.dens[1]*self.dens[0]+self.nums[0] B1 = 1 B2 = self.dens[1] yield Rational(A1,B1) for d,n in zip(self.dens[2:],self.nums[1:]): A1, A2 = A2, d*A2+n*A1 B1, B2 = B2, d*B2+n*B1 yield Rational(A1,B1) yield Rational(A2,B2)
def ratio_to_frac(S): """Convert a string of form a/b to Rational""" m = re.fullmatch("-?\d+\/\d+", S) is_neg = 1 if m: if m[0] == "-": is_neg = -1 m = m[1:] n, d = S.split("/") return Rational(int(n) * is_neg, int(d)) else: return None
def scientific_to_frac(S): """Convert a string formatted as e notation to Rational""" S = re.sub("e|⏨|\*\^", "E", S) m = re.fullmatch("-?\d\.\d+E-?\d+", S) if m: L, R = S.split("E") significand = term_dec_to_frac(L) power = Rational(10)**int(R) return significand * power else: return None
def term_dec_to_frac(S): """Convert a string representing a terminating decimal to Rational""" m = re.fullmatch("-?\d*\.\d*", S) is_neg = 1 if m: if S[0] == "-": is_neg = -1 S = S[1:] D = len(S) - first_where(S, ".") - 1 S = S.replace(".", "") return Rational(int(S) * is_neg, 10**D) else: return None
def as_rational(self): """Convert a generalized continued fraction to a Rational""" A1 = self.dens[0] A2 = self.dens[1]*self.dens[0]+self.nums[0] B1 = 1 B2 = self.dens[1] for d,n in zip(self.dens[2:],self.nums[1:]): A1, A2 = A2, d*A2+n*A1 B1, B2 = B2, d*B2+n*B1 return Rational(A2,B2)
def as_rational(self): """Convert a continued fraction to a Rational""" L = self.terms N = [1, L[0]] D = [0, 1] con = 2 while con < len(self) + 1: N.append(L[con - 1] * N[con - 1] + N[con - 2]) D.append(L[con - 1] * D[con - 1] + D[con - 2]) con += 1 return Rational(N[-1], D[-1])
def semiconvergents(self): """Rational semiconvergents""" a = self.terms Q = self.as_rational() prev = Rational(a[0]) for pos, val in enumerate(a): # Try appending the floor of half the next convergent semi = a[:pos] + [(val - 1) // 2 + 1] semi = CFrac(semi) # If it is worse than the last semiconvergent add 1 if abs(semi.as_rational() - Q) > abs(prev - Q): semi.terms[pos] += 1 while semi.terms[pos] <= val: yield prev prev = semi.as_rational() semi.terms[pos] += 1 yield Q
while True: w, f = R.mixed_form() L.append(w) if f == 0: break R = f.inv() return L if __name__ == '__main__': C = CFrac([8, 11, 4, 2, 7]) print(C) print(C.pretty_name) print(CFrac(Rational(3, 4))) print() print(C) C += [1, 1] print(C) C.insert(3, 5) print(C) C[1] = 7 print(C) C[2] += 9 print(C) del C[6] print(C) D = CFrac([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
out = [] while u != 0: a = (1/u).__ceil__() out.append(a) u = u*a-1 return out if __name__ == '__main__': print("GCD and LCM") a = Rational(13,6) b = Rational(3,4) G = rational_gcd(a,b) L = rational_lcm(a,b) print(f"gcd({a},{b}) = {G}") print(f"lcm({a},{b}) = {L}") print("\n\nRational Sequence") print(rational_seq(0,3,Rational(2,3))) print("\n\nRationalRound") R = Rational(214159,470) print(R) print(rational_round(R,100))
def mediant(a,b): a = cast_to_rational(a) b = cast_to_rational(b) return Rational(a.n+b.n,a.d+b.d)
def _rational_gcd(A,B): """Largest rational such that both A and B are integer multiples of it""" assert type(A) == Rational assert type(B) == Rational return Rational(gcd(A.n*B.d,A.d*B.n),(A.d*B.d))