こないだの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()