現在の閲覧者数:

N-Queens高速

ユニーク解が求まります。

♪ プログラムコード code

% nqueens_fast.pl

%---------------------------------------------------------------------------%
main :-
	set_gvar(count, 0),		% グローバル変数カウンタリセット 
	write( 'N = ' ),		% N は、N クイーン
	flush_output,			% 表示を反映する
	read( N ),			% N を入力
	statistics(runtime, _),		% 時間測定リセット
	solution( N, Queens ),		% N クイーンを求める
	x/y( Queens, Queens1, 1 ),	% [Y1,Y2,...]形式から[1/Y1,2/Y2,...]へ変換 
	min_size( Queens1, N ),		% 対称性:左上から下へ早く Q に出会った? 
	write( Queens ),		% ユニーク解を表示
	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, Queens ) :-
	range( 1, N, R ),
	solution( R, [], Queens ).
%---------------------------------------------------------------------------%
solution( [], Q, Q ).
solution( L, SafeQs, Q ) :-
	select_A( X, L, RestQs ),
	safe( [X|SafeQs] ),
	solution( RestQs, [X|SafeQs], Q ).
%---------------------------------------------------------------------------%
range(N,N,[N]) :- !.
range(M,N,[M|Ns]) :-
	M < N,
	M1 is M+1,
	range(M1,N,Ns).
%---------------------------------------------------------------------------%
permutation( [], [] ).
permutation( List, [Head|Tail] ) :-
	del( Head, List, List1 ),
	permutation( List1, Tail ).
%---------------------------------------------------------------------------%
select_A( Item, [Item|List], List ).
select_A( Item, [First|List], [First|List1] ) :-
	select_A( Item, List, List1 ).
%---------------------------------------------------------------------------%
safe( [] ).
safe( [Queen|Others] ) :-
	safe( Others ),
	noattack( Queen, Others, 1 ).
%---------------------------------------------------------------------------%
noattack( _, [], _ ).
noattack( Y, [Y1|Ylist], Xdist ) :-
	Y1 - Y  =\= Xdist,		% Up   check 
	Y  - Y1 =\= Xdist,		% Down check 
	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 ),
	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 ).
%---------------------------------------------------------------------------%




実行例:Ciao-Prolog で実行してみます。

love:uema% ciao                             [~/love/prolog]
Ciao 1.14.2-13646: Mon Aug 15 13:58:09 CEST 2011
?- [nqueens_fast].

yes
?- main.
N = 1.
[1] 1
CPU time = 0.148 msec

no
?- main.
N = 2.
CPU time = 0.026 msec

no
?- main.
N = 3.
CPU time = 0.041 msec

no
?- main.
N = 4.
[2,4,1,3] 1
CPU time = 0.159 msec

no
?- main.
N = 5.
[2,5,3,1,4] 1
[1,3,5,2,4] 2
CPU time = 0.746 msec

no
?- main.
N = 6.
[2,4,6,1,3,5] 1
CPU time = 1.477 msec

no
?- main.
N = 7.
[2,6,3,7,4,1,5] 1
[1,4,7,3,6,2,5] 2
[2,5,7,4,1,3,6] 3
[2,4,1,7,5,3,6] 4
[2,5,1,4,7,3,6] 5
[1,3,5,7,2,4,6] 6
CPU time = 5.82 msec

no
?- main.
N = 8.
[2,7,5,8,1,4,6,3] 1
[2,5,7,4,1,8,6,3] 2
[2,7,3,6,8,5,1,4] 3
[1,5,8,6,3,7,2,4] 4
[2,5,7,1,3,8,6,4] 5
[3,6,2,5,8,1,7,4] 6
[1,6,8,3,7,4,2,5] 7
[2,6,1,7,4,8,3,5] 8
[2,4,6,8,3,1,7,5] 9
[2,6,8,3,1,4,7,5] 10
[3,5,8,4,1,7,2,6] 11
[3,5,2,8,1,7,4,6] 12
CPU time = 20.049 msec

no
?- 

inserted by FC2 system