http://sheepman.parfait.ne.jp/20030418.html#p05
あ、sheepman さんのところに派生してる。
うーむ、おかしいなあ。 家だと lambda 使って遅延評価しても Hugs のほうが速いです。 GHC でコンパイルするとさらに激速。
~/c/test/haskell % cat lazy-tarai.rb
def tarai( x, y, z )
if x.call <= y.call
then y.call
else tarai(lambda { tarai(lambda{x.call-1}, y, z) },
lambda { tarai(lambda{y.call-1}, z, x) },
lambda { tarai(lambda{z.call-1}, x, y) })
end
end
puts tarai(lambda{48}, lambda{24}, lambda{0})
~/c/test/haskell % cat tarai.hs
module Main where
main :: IO ()
main = do
print (tarai 48 24 0)
tarai :: Int -> Int -> Int -> Int
tarai x y z
| x <= y = y
| otherwise = tarai (tarai (x-1) y z)
(tarai (y-1) z x)
(tarai (z-1) x y)
~/c/test/haskell % time ruby lazy-tarai.rb
48
ruby lazy-tarai.rb 0.44s user 0.00s system 104% cpu 0.419 total
~/c/test/haskell % time runhugs tarai.hs
48
runhugs tarai.hs 0.07s user 0.00s system 148% cpu 0.047 total
~/c/test/haskell % ghc -O -o tarai tarai.hs
~/c/test/haskell % time ./tarai
48
./tarai 0.00s user 0.00s system 0% cpu 0.000 total
つーか、GHC は速すぎだなあ。 定数畳み込みされてるのかと思ったんですが、 引数を大きくすると実行時間も増えていくところからすると ちゃんと計算しているようです。
3/22 の原先生の返り値をキャッシュするバージョンでも、 引数を (96,48,0) にすると Hugs に負けます。
~/c/test/haskell % time ruby memo-tarai.rb 96 ruby memo-tarai.rb 0.40s user 0.00s system 101% cpu 0.394 total ~/c/test/haskell % time runhugs tarai.hs 96 runhugs tarai.hs 0.10s user 0.00s system 107% cpu 0.093 total
ようするに、こないだのでは引数が小さすぎると。
改めて見ると今週はすごい流量が少ないな。 _vsnprintf の話は結局結論が出てないっぽいし、 bigdecimal 関係だけか。まあこういう週もあるやね。
lambdaだけでは計算量が完全な遅延評価ほど減らないように思います。
http://www.shiro.dreamhost.com/scheme/wiliki/wiliki.cgi?Scheme:たらいまわしべんち
おっと。リンク失敗しました。ごめんなさい。
http://www.shiro.dreamhost.com/scheme/wiliki/wiliki.cgi?Scheme%3a%a4%bf%a4%e9%a4%a4%a4%de%a4%ef%a4%b7%a4%d9%a4%f3%a4%c1&l=jp
原先生版のは配列生成のコストが結構ありそうな予感.あと,コンパイルによる最適かもあるんでしょうね.
配列生成は,tarai(96,48,0) なので,[x,y,z] が 35K 作られる.なら,作らないようにしよう.
というわけで,
$memo = {}
def tarai2( x, y, z )
getv(x,y,z) || putv(x,y,z)
end
def getv x,y,z
($memo[x] && $memo[x][y] && $memo[x][y][z])
end
def putv x,y,z
r = if x <= y
y
else
tarai2(
tarai2(x-1,y,z),
tarai2(y-1,z,x),
tarai2(z-1,x,y))
end
x = $memo[x] ||= {x=>{}}
#p x
y = x[y] ||= {y=>{}}
#p y
y[z] = r
end
=>
0.734000 0.000000 0.734000 ( 0.750000) # 原先生
0.375000 0.000000 0.375000 ( 0.391000) # 今回の
しかし,われながらダサい.
体裁を気にせずスピードを求めるなら:
def tarai( x, y )
if x <= y
then y
else
z = yield
tarai(tarai(x-1, y){z}, tarai(y-1, z){x}){ tarai(z-1, x){y} }
end
end
puts tarai(192, 96){0}