たらいを回すなら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だと思ってました。