たらいを回すならHaskellの話。Haskellすげえええ。
さて、PythonのMemoize*1版は誰かよろしこ、ということですのでためしに書いてみました。まずMemoizeしない版を、以下のようにいじっときます。
#!/usr/bin/python
import sys
def tak (x, y, z):
if x <= y:
return y
else:
return tak(tak(x - 1, y, z),
tak(y - 1, z, x),
tak(z - 1, x, y))
x, y, z = 10, 5, 0
if len(sys.argv) == 4:
x, y, z = map(int, sys.argv[1:])
print ('tak(%d, %d, %d) = %d' % (x, y, z, tak(x, y, z)))で、本命であるこれのMemoize版が次のコードです。
#!/usr/bin/python
import sys
taks = {}
def tak (x, y, z):
global taks
if x <= y:
return y
else:
if not taks.has_key((x,y,z)):
taks[(x,y,z)] = tak(tak(x - 1, y, z),
tak(y - 1, z, x),
tak(z - 1, x, y))
return taks[(x,y,z)]
x, y, z = 10, 5, 0
if len(sys.argv) == 4:
x, y, z = map(int, sys.argv[1:])
print ('tak(%d, %d, %d) = %d' % (x, y, z, tak(x, y, z)))で、この二つのベンチをとってみました。PowerBook G4 (867 MHz, 640 MB RAM)の環境でOS標準のPython使ってます。
tak.py
tak(12, 6, 0) = 12
21.95 real 19.22 user 0.31 sys
tak_memoize.py
tak(12, 6, 0) = 12
0.18 real 0.05 user 0.08 sysなんか相当はやくなったー!さすがにHaskellにはおよばなさそう。しかし、Memoize版Pythonもかなりがんばってます。比較のために、うちの環境でほかの言語のたらい回しをベンチしてみました。PerlとRubyのコードは、たらいを回すならHaskellからお借りしました。
tak_memoize.pl
tak(100, 50, 0) = 100
0.54 real 0.44 user 0.04 sys
tak_memoize.rb
tak(100, 50, 0) = 100
0.91 real 0.79 user 0.04 sys
tak_memoize.py
tak(100, 50, 0) = 100
0.39 real 0.27 user 0.08 sysなかなかやるぜ、Python。
あと、これの元ネタである、C言語による最新アルゴリズム事典が手元にあったのでp185を参照してみたんですが、
たらいまわし関数
再帰的に定義された次のような関数。特に用途はない。
あ、そうなんですか…。なんか、スペシャルなものではないのね。すごい膨大な関数呼び出しが起こる例みたいなもん?
*1:ずっとMemorizeだと思ってました。