前の版(高速)より速くなりました。ユニーク解が求まります。
♪ プログラムコード code
% nqueens_fast_more.pl %---------------------------------------------------------------------------% main :- set_gvar(count, 0), % グローバル変数カウンタリセット write( 'N Queens!!\n' ), % パズル名 write( 'N = ' ), % N = N_Queens flush_output, % 表示を反映する read( N ), % N を読み込む statistics(runtime, _), % 時間測定リセット solution( N, Q ), % N_Queens を求める x/y( Q, Q1, 1 ), % [Y1,Y2,...]形式から[1/Y1,2/Y2,...]へ変換 min_size( Q1, N ), % 対称性:左上から下へ早く Q に出会った? write( Q ), % ユニーク解を表示 gvar(count, CNT), % グローバル変数カウンタ値取得 write( ' ' ), % 空白を表示 write( CNT ), % 解の番号を表示 flush_output, % 表示を反映する nl, % 改行 fail. % 失敗駆動:別解を求める %---------------------------------------------------------------------------% main :- statistics(runtime, [_,T]), % 時間測定計時 write('CPU time = '), % write(T), % write(' msec'), % nl, % !, % fail. % %---------------------------------------------------------------------------% solution( N, Q ) :- range( 1, N, R ), solution( R, [], Q ). solution( [], Q, Q ) :- !. solution( R, Q, Ans ) :- select( A, R, R1 ), noattack( A, Q, 1 ), solution( R1, [A|Q], Ans ). range( M, M, [M] ):- M > 0, !. range( N, M, [N|R] ) :- N > 0, N1 is N + 1, range( N1, M, R ). noattack( _, [], _ ). noattack( Y, [Y1|Ylist], Xdist ) :- Y1 - Y =\= Xdist, Y - Y1 =\= Xdist, Dist1 is Xdist + 1, noattack( Y, Ylist, Dist1 ). %---------------------------------------------------------------------------% % グローバル変数エンジン gvar(Name, X)がグローバル変数 set_gvar(Name, X) :- nonvar(Name), retract(gvar(Name, _)), !, asserta(gvar(Name, X)). set_gvar(Name, X) :- nonvar(Name), asserta(gvar(Name, X)). %---------------------------------------------------------------------------% % -- 求まった解から上下左右反転解を求め -- % % -- 大小関係を定義してより小さければ成功する -- % % min_size( Queens ): 左右上下反転の同一解の内、最小解なら成功する。 min_size( Queens, N ) :- change_90( Queens, Queens90, N ), ck_min( Queens, Queens90 ), change_90( Queens90, Queens180, N ), ck_min( Queens, Queens180 ), change_90( Queens180, Queens270, N ), ck_min( Queens, Queens270 ), change_l/r( Queens, QueensM0, N ), ck_min( Queens, QueensM0 ), change_90( QueensM0, QueensM90, N ), ck_min( Queens, QueensM90 ), change_90( QueensM90, QueensM180, N ), ck_min( Queens, QueensM180 ), change_90( QueensM180, QueensM270, N ), ck_min( Queens, QueensM270 ), gvar(count, CNT), CNT1 is CNT + 1, set_gvar(count, CNT1). %---------------------------------------------------------------------------% % -- チェス盤に大小関係を定義して、より小さければ成功する -- % ck_min( A, B ) :- % sort_xy( B, B1 ), % バブルソート % 9233個 N=13,CPU time = 81057 msec quicksort_xy( B, B1 ), % クイックソート % 9233個 N=13,CPU time = 63292 msec ck( A, B1 ). %---------------------------------------------------------------------------% % 左側から縦に上から下に向かって、2つのチェス盤をしらべていき、より早くクイーンに出会った ほうが小さいことにする。 ck( [], [] ). ck( [_/Y1|L1], [_/Y2|L2] ) :- Y1 =:= Y2 -> ck( L1, L2 ) ; Y1 < Y2. %---------------------------------------------------------------------------% % -- [Y1,Y2,....]形式から[1/Y1,2/Y2,....]形式へ変換 -- % x/y( [], [], _ ):-!. x/y( [A|L1], [B|L2], N ) :- B = N/A, N1 is N + 1, x/y( L1, L2, N1 ). %---------------------------------------------------------------------------% % -- 左右反転:ひっくり返す -- % change_l/r( [], [], _ ) :- !. change_l/r( [A/B|L1], [X/Y|L], N ) :- N1 is N + 1, X is N1 - A, Y is B, change_l/r( L1, L, N ). %---------------------------------------------------------------------------% % -- 反時計90度回転 -- % change_90( [], [], _ ) :- !. change_90( [A/B|L1], [X/Y|L], N ) :- N1 is N + 1, X is B, Y is N1 - A, change_90( L1, L, N ). %---------------------------------------------------------------------------% % -- [X1/Y1,X2/Y2,...]形式を X を対象にしてソート。[1/Y1,2/Y2,...]へ -- % % --- バブルソート --- % sort_xy( A, B ) :- swap( A, C ), !, sort_xy( C, B ). sort_xy( A, A ). swap( [A,B|C], [B,A|C] ) :- A = A1/_, B = B1/_, A1 > B1. swap( [A|B], [A|C] ) :- swap( B, C ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % -- [X1/Y1,X2/Y2,...]形式を X を対象にしてソート。[1/Y1,2/Y2,...]へ -- % % --- クイックソート --- % quicksort_xy( List, Sorted ) :- quicksort_xy_2( List, Sorted-[] ). %---------------------------------------------------------------------------% quicksort_xy_2( [], S-S ). quicksort_xy_2( [A|Tail], B1-Z2 ) :- sprit( A, Tail, Small, Big ), quicksort_xy_2( Small, B1-[A|B2] ), quicksort_xy_2( Big, B2-Z2 ). %---------------------------------------------------------------------------% sprit( _, [], [], [] ). sprit( A, [B|Tail], [B|Small], Big ) :- % A > B, !, A = X1/_, B = X2/_, X1 > X2, !, sprit( A, Tail, Small, Big ). sprit( A, [B|Tail], Small, [B|Big] ) :- sprit( A, Tail, Small, Big ). %---------------------------------------------------------------------------%
実行例
love:uema% swipl -qs nqueens_fast_more.pl [~/love/prolog] ?- main. N Queens!! N = 13. [1,9,6,8,3,11,13,5,10,12,7,4,2] 1 [1,9,7,5,8,11,13,6,3,12,10,4,2] 2 [1,7,10,6,9,5,13,11,3,8,12,4,2] 3 [1,9,6,10,7,11,3,5,13,8,12,4,2] 4 [1,8,4,7,12,10,5,13,11,9,3,6,2] 5 [1,8,5,9,4,13,10,12,7,11,3,6,2] 6 [1,9,13,3,7,4,10,12,5,11,8,6,2] 7 [1,9,5,10,4,11,3,12,7,13,8,6,2] 8 [1,7,4,8,11,13,10,3,5,12,9,6,2] 9 [1,10,4,7,11,13,3,5,8,12,9,6,2] 10 [1,8,11,5,3,13,9,12,4,7,10,6,2] 11 [1,8,4,12,9,13,5,3,11,7,10,6,2] 12 [1,8,11,9,4,10,5,3,12,7,13,6,2] 13 [1,3,10,6,9,11,13,4,12,8,5,7,2] 14 <略> [1,4,8,13,9,7,3,11,6,2,5,10,12] 9227 [1,6,4,9,13,8,3,11,2,7,5,10,12] 9228 [1,3,8,6,9,2,13,11,4,7,5,10,12] 9229 [1,4,7,9,13,3,8,11,5,2,6,10,12] 9230 [2,4,6,1,9,11,13,8,3,5,7,10,12] 9231 [1,6,4,7,13,11,9,2,5,3,8,10,12] 9232 [1,3,5,7,9,11,13,2,4,6,8,10,12] 9233 CPU time = 82175 msec false. ?-
13クイーンが82秒(1分22秒)で求まりました。超高速です(^_^;)
次のように前の版では、13クイーンが、445秒(7分25秒)かかります。
[1,4,7,5,10,2,13,11,3,8,6,9,12] 9221 [2,4,8,11,5,1,10,6,13,3,7,9,12] 9222 [1,4,6,8,3,11,13,2,10,5,7,9,12] 9223 [1,7,5,8,11,13,3,9,6,4,2,10,12] 9224 [1,5,7,11,13,8,4,9,3,6,2,10,12] 9225 [1,8,4,13,7,9,11,5,2,6,3,10,12] 9226 [1,4,8,13,9,7,3,11,6,2,5,10,12] 9227 [1,6,4,9,13,8,3,11,2,7,5,10,12] 9228 [1,3,8,6,9,2,13,11,4,7,5,10,12] 9229 [1,4,7,9,13,3,8,11,5,2,6,10,12] 9230 [2,4,6,1,9,11,13,8,3,5,7,10,12] 9231 [1,6,4,7,13,11,9,2,5,3,8,10,12] 9232 [1,3,5,7,9,11,13,2,4,6,8,10,12] 9233 CPU time = 445840 msec false. ?-