http://ll.jus.or.jp/2006/blog/doukaku2 の話。3回目。
g:haskell:id:jmk:20060712:1152721034 でやっていた逆展開を私もやってみる。
ただし、そのまんまやると h(100)の計算が終わらないようなので、終わるように工夫して:
def collatz( n ) done=3 # 1,2,4 は計算済みとする wait=[ [8,4] ] # 1,2,4 に入れるのは 8 だけ def wait.special_insert a # 二分探索で整列しつつ挿入 r=[0,size] while r[0]+1<r[1] do m=( r[0]+r[1] )/2 r[ -self[m][0] <= -a[0] ? 0 : 1 ] = m end insert( r[ self.empty? || -a[0] <= -self[r[0]][0] ? 0 : 1 ], a ) end while done<n a = wait.pop if a[0]<=n then done+=1 max = a if !max || max[1]<a[1] end wait.special_insert [ a[0]*2, a[1]+1 ] wait.special_insert [ (a[0]-1)/3, a[1]+1 ] if 3==(a[0]-1)%6 end max[0] end p collatz(100)
これで、手元の処理系で collatz(100) は1秒未満。1000 でも現実的な時間で終わる。10000 は試してない。
C++ の std::set のような、ソート済み配列が ruby にはないのでそれも実装する必要があったのが面倒だった。
std::set のためだけに C++ で書こうかとも思ったが、やっぱり ruby にした。
最初は挿入関数を書かずに push してから sort! していたんだが、やっぱりそれはあんまりだと思ってちゃんと書いた。そしたら劇的に速くなった。当たり前。
1,2,4 を計算済みにしている関係で、 引数の値が 3以下だとうまくいかない。まあドンマイということで。