ここまでやると、あとはテキストに従って書くだけ。まず、次のようにimproveという関数を定義して、新たな近似を作る。
def order(s):
a = s.next()
b = s.next()
c = s.next()
return int(log((a - c) / (b - c) - 1, 2.0) + 0.5)def elimerror(n, l):
b = l.next()
while True:
a = b
b = l.next()
yield (b * (2 ** n) - a) / (2 ** n - 1)def improve(s):
return elimerror(order(s), s)def diff(eps, f, x):
return within(eps, improve(differentiate(1.0, f, x)))
と思ったらそうはいかなかった。どこがまずいかというと、elimerrorの引数でsが2回使われているところである。orderのためにsを3回使って、そのあとにelimerrorを使ったときは4つ目からになってしまう。前にもこんなことがあった。
そこで、先頭に戻れるようにするクラスを作る。
class inf_list:
def __init__(self, g):
self.estimated = [ ]
self.pos = 0
self.g = g
def __iter__(self):
return self
def next(self):
if self.pos < len(self.estimated):
ret = self.estimated[self.pos]
else:
ret = self.g.next()
self.estimated.append(ret)
self.pos += 1
return ret
def rewind(self):
self.pos = 0
一度評価したらestimatedに入れる。posを持っておき、これがestimatedの長さ以上なら評価する。
def counter():
n = -1
while True:
n += 1
print "counter :", n
yield ng = inf_list(counter())
for i in range(2):
print g.next()
g.rewind()
for i in range(3):
print g.next()
これを実行すると、
counter : 0
0
counter : 1
1
0
1
counter : 2
2
これでようやく正しい加速バージョンが書ける。
def order(s):
a = s.next()
b = s.next()
c = s.next()
s.rewind() // add
return int(log((a - c) / (b - c) - 1, 2.0) + 0.5)def improve(s):
g = inf_list(s)
return elimerror(order(g), g)
これで収束が速くなる。
さらに収束を加速するために、improveを繰り返し適用し、それぞれの2番目の値を取り出す。
def second(l):
l.next()
return l.next()def super(s):
return Map(second, Repeat(improve, s))def diff(eps, f, x):
return within(eps, super(differentiate(1.0, f, x)))
いくつかにinf_listをつけなければならない。
これであってるかなあ。
ソースは下で、結果はこう。
0.869295562859
0.866208623819
0.866030200973
0.866025459008
0.866025404029
0.866025404029
0.866025403784
from math import *
from functools import partialdef Repeat(f, a):
yield a
while True:
a = f(a)
yield adef Map(f, l):
while True:
yield f(l.next())def halve(x):
return x / 2def easydiff(f, x, h):
return (f(x + h) - f(x)) / hdef differentiate(h0, f, x):
return inf_list(Map(partial(easydiff, f, x), Repeat(halve, h0)))def order(s):
a = s.next()
b = s.next()
c = s.next()
s.rewind()
return int(log((a - c) / (b - c) - 1, 2.0) + 0.5)def elimerror(n, l):
b = l.next()
while True:
a = b
b = l.next()
yield (b * (2 ** n) - a) / (2 ** n - 1)class inf_list:
def __init__(self, g):
self.estimated = [ ]
self.pos = 0
self.g = g
def __iter__(self):
return self
def next(self):
if self.pos < len(self.estimated):
ret = self.estimated[self.pos]
else:
ret = self.g.next()
self.estimated.append(ret)
self.pos += 1
return ret
def rewind(self):
self.pos = 0def improve(s):
s.rewind()
g = inf_list(s)
return inf_list(elimerror(order(g), g))def second(l):
l.next()
return l.next()def super(s):
return Map(second, Repeat(improve, s))def within(eps, l):
b = l.next()
while True:
a = b
b = l.next()
print b
if abs(a - b) <= eps:
return bdef diff(eps, f, x):
return within(eps, super(differentiate(1.0, f, x)))def f(x):
return sqrt(x * x + x + 1)x = 1.0
eps = 1e-7
print diff(eps, f, x)
print sqrt(3) / 2 # reference