バックトラック法 ナイト巡回問題は、与えられたマスを、ナイトが連続して飛び続けることで、 全てのマスを1回ずつ訪れる経路を求める問題である。 行儀の良くないナイト巡回問題を解いてみます。
create table ban(X,Y) as select 1,1 from dual union select 1,2 from dual union select 1,3 from dual union select 1,4 from dual union select 1,5 from dual union select 2,1 from dual union select 2,2 from dual union select 2,3 from dual union select 2,4 from dual union select 2,5 from dual union select 3,1 from dual union select 3,2 from dual union select 3,3 from dual union select 3,4 from dual union select 3,5 from dual union select 4,1 from dual union select 4,2 from dual union select 4,3 from dual union select 4,4 from dual union select 4,5 from dual union select 5,1 from dual union select 5,2 from dual union select 5,3 from dual union select 5,4 from dual union select 5,5 from dual;
select sys_connect_by_path(to_char(X) || to_char(Y),'-') as path
from ban
where connect_by_IsLeaf = 1
and Level = 25
and RowNum <=10
start with 1 = all(X,Y)
connect by nocycle (prior X,prior Y)
in ((X+2,Y-1),(X+2,Y+1),
(X-2,Y+1),(X-2,Y-1),
(X+1,Y+2),(X+1,Y-2),
(X-1,Y+2),(X-1,Y-2));
declare
masu constant pls_Integer := 5;
type InfoDef is record(
X pls_Integer,
Y pls_Integer,
LV pls_Integer,
Path varchar2(1000)
);
stackP pls_Integer := 0;
type StackClass is table of InfoDef index by binary_integer;
st StackClass;
StaInfo InfoDef;
priInfo InfoDef;
willPush InfoDef;
work varchar2(20);
LoopChk pls_Integer :=0;
isOK boolean;
procedure push(pInfoDef InfoDef) is
begin
st(stackP) := pInfoDef;
stackP := stackP+1;
end;
function pop return InfoDef is
begin
stackP := stackP-1;
return st(stackP);
end;
function isEmpty return boolean is
begin
return stackP = 0;
end;
begin
StaInfo.X := 1; StaInfo.Y := 1;
StaInfo.LV := 1; StaInfo.Path := '(1,1)';
push(StaInfo);
while (isEmpty = false) loop
priInfo := pop();
for X in 1..masu Loop
for Y in 1..masu Loop
if (((priInfo.X+2 = X and priInfo.Y+1 = Y)
or (priInfo.X+2 = X and priInfo.Y-1 = Y)
or (priInfo.X+1 = X and priInfo.Y+2 = Y)
or (priInfo.X+1 = X and priInfo.Y-2 = Y)
or (priInfo.X-2 = X and priInfo.Y+1 = Y)
or (priInfo.X-2 = X and priInfo.Y-1 = Y)
or (priInfo.X-1 = X and priInfo.Y+2 = Y)
or (priInfo.X-1 = X and priInfo.Y-2 = Y)) and priInfo.LV < masu*masu) then
work := '(' || to_char(X) || ',' || to_char(Y) || ')';
--未到着ノードならpush
if (instr(priInfo.Path,work) = 0) then
willPush.Path:= priInfo.Path || '-' || work;
willPush.X := X; willPush.Y := Y;
willPush.LV := priInfo.LV+1;
push(willPush);
end if;
end if;
end Loop;
end Loop;
--合格経路なら表示
if (priInfo.LV = masu*masu) then
isOK := true;
for X in 1..masu Loop
for Y in 1..masu Loop
work := '(' || to_char(X) || ',' || to_char(Y) || ')';
if (instr(priInfo.Path,work) = 0) then
isOK := false;
end if;
end Loop;
end Loop;
if (isOK) then
DBMS_Output.Put_Line(priInfo.Path);
end if;
end if;
LoopChk := LoopChk+1;
if (LoopChk=99999) then
DBMS_Output.Put_Line('CountStop!!!');
exit;
end if;
end Loop;
end;
/
実行結果
(1,1)-(3,2)-(5,3)-(4,5)-(2,4)-(1,2)-(3,3)-(4,1)-(2,2)-(1,4)-(3,5)-(5,4)-(4,2)-(2,1)-(1,3)-(2,5)-(4,4)-(5,2)-(3,1)-(2,3)-(1,5)-(3,4)-(5,5)-(4,3)-(5,1) (1,1)-(3,2)-(5,3)-(4,5)-(2,4)-(1,2)-(3,1)-(5,2)-(4,4)-(2,5)-(1,3)-(2,1)-(4,2)-(5,4)-(3,3)-(4,1)-(2,2)-(1,4)-(3,5)-(2,3)-(1,5)-(3,4)-(5,5)-(4,3)-(5,1) (1,1)-(3,2)-(5,3)-(4,5)-(2,4)-(1,2)-(3,1)-(5,2)-(4,4)-(2,5)-(1,3)-(2,1)-(3,3)-(4,1)-(2,2)-(1,4)-(3,5)-(5,4)-(4,2)-(2,3)-(1,5)-(3,4)-(5,5)-(4,3)-(5,1) (1,1)-(3,2)-(5,3)-(4,5)-(2,4)-(1,2)-(3,1)-(5,2)-(3,3)-(4,1)-(2,2)-(1,4)-(3,5)-(5,4)-(4,2)-(2,1)-(1,3)-(2,5)-(4,4)-(2,3)-(1,5)-(3,4)-(5,5)-(4,3)-(5,1) CountStop!!!
PL/SQLよりも、Javaのほうが速く終了しましたねぇ