昨日の続き。
id:Cryolite:20050908:p3 のアルゴリズムも実装してみた。
こんな感じ:
require 'set' L = { #略 } PLAYERS = L.keys def each_combi( a0, n ) return if a0.size<n || n<0 if a0.size==n yield a0 else a = a0.to_a tail = a[1..-1] each_combi( tail, n-1 ){ |x| yield( [a[0]]+x ) } each_combi( tail, n ){ |x| yield( x ) } end end def log2( x ) return x<=1 ? 0 : log2( x/2 )+1 end subt = (0..log2( PLAYERS.size )).map{ Set.new } each_combi(PLAYERS, 2 ){ |x,y| xwin=L[x][ y[0]-?A ]=='o' ywin=L[y][ x[0]-?A ]=='o' if xwin==ywin raise "which will win? : #{x},#{y}" elsif xwin subt[0] += [ [ x, y ] ] elsif ywin subt[0] += [ [ y, x ] ] else raise "unexpected." end } def cone( rule, a, b ) def _cone( x, y ) r = [x,y].flatten.uniq return r.size == x.size + y.size ? r : nil end if a[0]==rule[0] && b[0]==rule[1] _cone( a, b ) elsif b[0]==rule[0] && a[0]==rule[1] _cone( b, a ) else nil end end def progress( subt, n, target ) each_combi( subt[ n-1 ], 2 ){ |x| subt[0].each{ |z| r = cone( z, x[0], x[1] ) next if !r next if r[0]!=target && r.member?( target ) # 枝刈り subt[n] += [r] } } end log2(PLAYERS.size).times{ |x| progress( subt, x, 'A' ) } p subt[log2(PLAYERS.size)-1]
実行してみると、すごく遅い。
最初、枝刈りなしで書いてみたらあんまりだったので、枝刈りをちょっと入れてみた。上記ソースに「枝刈り」と書いている場所がそれだ。
n=8 だと、多少枝刈りしているこのアルゴリズムの方が、全く枝刈りせずに馬鹿正直に総当たりするよりもずっと遅い。
n=16 は、実行していないが、正解までたどり着けそうな気がしない。
特に、正解がたくさんあるときにすごく遅くなりそうだと思う。
思うだけで、調べてはいないが。それと。正解が全くないことを検証するのは速いのかもしれないとはちょっと思う。これも思うだけ。
とはいえ。each_combi が遅すぎるという批判は正しいと思う。スタックに積み過ぎ。
それと。この実装が、Cryolite さんの意図にそったものなのかどうか、あまり自信がなかったりする。