連分数 (continued fraction)
こないだのJune Long ChallengeのEuler Sumを考えてる時に
Mark Jason Dominus氏のスライド
Arithmetic with Continued Fractions
を読んだ時のメモ。
連分数とかいじるのって多分昔SICPを読んだ時以来。
のような記法を用いる。
]
]
のような規則的な形に展開される。
連分数に対し、に相当する連分数を求めるのは
に、例えば なら
を右から食わせて
になった時点で随時 を取り出して に差し替えていくことで求められる。
10進表記が得たいなら、1つ取り出した後の残りに すなわち を適用していけば良い。
連分数に対し、に相当する連分数を求めるのは
に
側から
側から
を右から食わせて
になった時点で随時 を取り出して に差し替えていくことで求められる。
次にどちらを食わせるかは
ならから、そうでなければから。
みたいな演算は、分子・分母をそれぞれ計算してから…でなくても、なので
で一発で求めることができる。
以下、pythonで実装してみたやつ
] みたいな規則的な数列を生成するジェネレータを用意する
10進展開とかしようとすると500桁も行かないうちに
RuntimeError: maximum recursion depth exceeded
とか言われるので sys.setrecursionlimit() の出番。
$ python renbunsuu e 1000 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413596629043572900334295260595630738132328627943490763233829880753195251019011573834187930702154089149934884167509244761460668082264800168477411853742345442437107539077744992069551702761838606261331384583000752044933826560297606737113200709328709127443747047230696977209310141692836819025515108657463772111252389784425056953696770785449969967946864454905987931636889230098793127736178215424999229576351482208269895193668033182528869398496465105820939239829488793320362509443117301238197068416140397019837679320683282376464804295311802328782509819455815301756717361332069811250996181881593041690351598888519345807273866738589422879228499892086805825749279610484198444363463244968487560233624827041978623209002160990235304369941849146314093431738143640546253152096183690888707016768396424378140592714563549061303107208510383750510115747704171898610687396965521267154688957035035
これで所要時間は5秒ぐらい。
分子・分母ともに巨大な整数になるので、np.dot()でポンみたいなわけにはいかない...
#!/usr/bin/env python # -*- encoding: utf-8 -*- # # > python renbunsuu.py e 100 # import sys import re import click import math class ContUnary: def __init__(self,a=0,b=1,c=1,d=0): self.args = (a,b,c,d) def input(self, p): a,b,c,d = self.args # ab 01 # cd 1p self.args = (b, a+b*p, d, c+d*p) def ready(self): a,b,c,d = self.args if c>0 and d>0: return a/c == b/d else: return False def output(self): # 01 ab # 1-q cd assert self.ready() a,b,c,d = self.args q = a/c self.args = (c, d, a%c, b%d) return q def apply(self, g, stop=-1): # self.g = g cnt = 0 while True: while not self.ready(): self.input(g.next()) yield self.output() cnt += 1 if cnt == stop: break def mul(self,e,f,g,h): a,b,c,d = self.args self.args = (a*e+b*g, a*f+b*h, c*e+d*g, c*f+d*h) def e_gen(): yield 2 k = 2 while True: yield 1 yield k k += 2 yield 1 def phi_gen(): while True: yield 1 def root_gen(n): ren_dic = { # 2: [1, 2], 3: [1, 1, 2], # 5: [2, 4], 6: [2, 2, 4], 7: [2, 1, 1, 1, 4], # 10: [3, 6], 19: [4, 2, 1, 3, 1, 2, 8], 29: [5, 2, 1, 1, 2, 10], 43: [6, 1, 1, 3, 1, 5, 1, 3, 1, 1, 12], 54: [7, 2, 1, 6, 1, 2, 14], 76: [8, 1, 2, 1, 1, 5, 4, 5, 1, 1, 2, 1, 16], 94: [9, 1, 2, 3, 1, 1, 5, 1, 8, 1, 5, 1, 1, 3, 2, 1, 18], 1000: [31, 1, 1, 1, 1, 1, 6, 2, 2, 15, 2, 2, 6, 1, 1, 1, 1, 1, 62], } q = int(math.sqrt(n - 1)) if q*q+1 == n: yield q q *= 2 while True: yield q elif n in ren_dic: a0, rest = ren_dic[n][0], ren_dic[n][1:] assert rest[-1] == a0 * 2 yield a0 while True: for a in rest: yield a def render(src, base=10): U = ContUnary(b=1) gen = U.apply(src) yield gen.next() while True: gen = ContUnary(a=base,b=0,c=0,d=1).apply(gen) yield gen.next() @click.command() @click.argument('type', type=str) @click.argument('nterms', type=int, default=10) @click.argument('base', type=int, default=10) def main(type, nterms, base): if type == 'e': gen = e_gen() elif type == 'phi': gen = phi_gen() elif re.match(r'\d+', type): gen = root_gen(int(type)) else: return sys.setrecursionlimit(1000000) gen = render(gen, base) sys.stdout.write('%d.' % gen.next()) for i, d in enumerate(gen): if i == nterms-1: break sys.stdout.write('%d' % d) sys.stdout.flush() sys.stdout.write('\n') return if __name__ == '__main__': main()