Transcript Document

数値・記号処理(11)
Prolog の実行制御
慶應義塾大学理工学部
櫻井彰人
等号そして否定について
等価性を調べる方法はいくつかある.
X = Y
項 X とY が unify するとき成功.
X is Y
X =:= Y
X =\= Y
式 Y の計算結果が項 X の値と照合するとき成功.
2つの式の計算結果が照合するとき成功.
2つの式の計算結果が照合しないとき成功.
X == Y
X \== Y
2項が文字通り同じとき (literal equality) に成功
literal equality = 構造が同じで要素が同じ名前.
2項が文字通りには同じでないとき成功.
\+ Goal
not( Goal )
Goal が失敗したとき成功
同上
等号の例
| ?- 3+4 = 4+3.
no % 項として比較
| ?- 3+4 = 3+4.
yes
| ?- X = 4+3.
X = 4+3 ?
yes
| ?- X is 4+3.
X = 7 ?
yes
| ?- 3+4 is 4+3.
no % 左は項であるべし
| ?- 3+4 =:= 4+3.
yes % 両方の値を計算する
| ?- 3+4 =\= 4+3.
no
| ?- 3+4
no
| ?- 3+4
yes
| ?- 3+X
X = 4 ?
| ?- 3+X
no
== 4+3.
\== 4+3.
= 3+4.
yes
== 3+4.
| ?- \+ 3+4 == 4+3.
yes
Prolog における評価方法と trace
ゴール G を call するには:
1. G と照合する最初の節頭部を捜す:
1. 全変数を対応させながら、変数束縛する,
2. 本体のゴールを call する;
3. すべてが成功なら, G は succeeds
(そして exits).
2. 失敗なら次の節を試す;
3. 次の節がなければ, ゴールG は fail.
ゴールが fail すると:
最も最近成功したゴールを redo する
ゴールを redo するには:
1.
2.
3.
この前成功したときの変数束縛は解除;
当該ゴールに対し、未試行の節を試す;
もしなければ, ゴールは失敗.
Byrd Box model
tracer が用いる実行モデル.
Lawrence Byrd による.
EXIT
CALL
GOAL
FAIL
Exception (error)
REDO
ゴールの Redo
fact(b,1).
fact(b,2).
a :- fact(b,N), fact(c,N).
|?- a.
CALL
EXIT
fact(b,N)
N=1
ゴールの Redo (2)
fact(b,1).
fact(b,2).
a :- fact(b,N), fact(c,N).
|?- a.
CALL
EXIT
fact(b,N)
CALL
N=1
REDO
FAIL
fact(c,1)
ゴールの Redo (3)
fact(b,1).
fact(b,2).
a :- fact(b,N), fact(c,N).
|?- a.
EXIT
N=1
EXIT
CALL
fact(b,N)
N=2
REDO
ゴールの Redo (4)
fact(b,1).
fact(b,2).
a :- fact(b,N), fact(c,N).
|?- a.
no.
EXIT
N=1
EXIT
CALL
FAIL
fact(b,N)
CALL
N=2
REDO
FAIL
fact(c,2)
Prolog の粘り腰
副目標(sub-goal)が fail すると, Prolog は, 最も最近に成
功したゴールに backtrack し、他に照合するものを探す.
この副目標に照合するものがなければ, 再び backtrack す
る; ただし、親のゴールに戻る前に, 副目標の retry は全て
やりおえる.
call はどの節頭部とも照合を試みる.
新しい インスタンス化
redo はかつて照合したものは無視する.
a:- b, c, d, e, f, g, h, I, j .
a:- b, c, d, e, f, g, h, I, j .
Succeed
Fail
Redo
Backtrack
a:- b, c, d, e, f, g, h, I, j .
Cut !
backtrack を抑止することは, redo できる副目標を制
御することにより行う。これは、 cut = ! で行う.
節本体内では、一つのゴールのように扱う.
cut は callされると成功する, しかし backtrack 時に
それを redo しようとすると, 親のゴール(当該 cut を含む
節本体の頭部に照合したゴール)を fail させる.
当該述語中でなした決定に commit することである.

cut の前や後では、制限なく backtrack できる。しかし,
backtrack は cut を通りぬけることはできない.
immediate fail
a:- b, c, d, e, !, f, g, h, I, j .
a:- b, c, d, e, !, f, g, h, I, j .
親ゴールを fail する
a:- b, c, d, e, !, f, g, h, I, j .
a:- b, c, d, e, !, f, g, h, I, j .
a:- k.
a:- k.
a:- m .
これらはないかの
ように扱われる
a:- m .
この節となした選択に
commit する
Cut は call されれば成功し, 親ゴールが起動され
てから当該 cut が起動されるまでの間になされた選
択に Prolog システムを commit させる.
これには, 当該 cut を含む節への commit も含む.
= 当該ゴールが成功するのは, この節が成功するときに限られる.
Cut を経由する backtrack が試みられると


当該節は直ちに fail し,
他の候補節を探索することはしない.
互いに排他的な節
Cut を使用するのは, ある節集合が互いに排他的な場合(一節
が成功すれば, 他の節は成功しない)のみに限るべきである.
もしある節集合が互いに排他的であれば, 一度成功した節以
外の節を Prolog システムが試行する必要はない
節本体に cut を入れるということは, その節に commit すること
である.

Cut を本体の先頭に置くことは, 頭部の unification が終了するや否やそ
の節に commit することを意味する.
a(1,X):- !, b(X), c(X).

Cut を本体のどこか(末尾も含め)におくことは, cut 以前の副ゴールが成
功したときに, 当該節に commit することを表す.
a(_,X):- b(X), c(X), !.
互いに排他的な節 (2)
f(X,0):- X < 3.
f(X,1):- 3 =< X, X < 6.
f(X,2):- 6 =< X.
|?- trace, f(2,N).
1
1 Call:
f(2,_487) ?
2
2 Call: 2<3 ?
2
2 Exit: 2<3 ? ?
1
1 Exit: f(2,0) ?
N = 0 ? ;
1
1 Redo: f(2,0) ?
3
2 Call: 3=<2 ?
3
2 Fail: 3=<2 ?
4
2 Call: 6=<2 ?
4
2 Fail: 6=<2 ?
1
1 Fail:
f(2,_487) ?
no
安全な cut !
f(X,0):- X < 3, !.
f(X,1):- 3 =< X, X < 6, !.
f(X,2):- 6 =< X.
この点に到達すれば, もう他の
節を試みることはない.
|?- trace, f(2,N).
1
1 Call:
f(2,_487) ?
2
2 Call: 2<3 ?
2
2 Exit: 2<3 ? ?
1
1 Exit: f(2,0) ?
N = 0 ? ;
no
注意: cut があろうとなかろうと, 答えは同じである.


その理由は, cut はプログラムの論理的動作を変えることはないからで
ある.
cut が変えるのは手続き的動作である. すなわち, どのゴールをいつ調
べるかということを変える.
これは安全なcut green cut と呼ばれる. Cut の正しい用法である.
安全な cut を用いるときには, 当該節集合が実際に互いに排他
的であることを確かめるべき.
危険な cut !
f(X,0):- X < 3, !.
f(X,1):- 3 =< X, X < 6, !.
f(X,2):- 6 =< X.
Redundant ではないか?
| ?- f(7,N).
1
1 Call:
f(7,_475) ?
2 Call: 7<3 ?
2 Fail: 7<3 ?
2 Call: 3=<7 ?
3
2 Exit:
4
2 Call:
4
2 Fail:
5
2 Call:
5
2 Exit:
1
1 Exit:
N = 2 ?
yes
2
2
3
3=<7 ?
7<6 ?
7<6 ?
6=<7 ?
6=<7 ?
f(7,2) ?
節は互いに排他的であり, 並んでいる順序は, より上の節がfailするのは
ある条件が成立したときのみということが分かっているので.
効率向上のために, 余分な副ゴールの実行を省略したい.
危険な cut !
f(X,0):- X < 3.
f(X,1):- X < 6.
f(X,2).
f(X,0):- X < 3, !.
f(X,1):- X < 6, !.
f(X,2).
| ?- f(7,N).
1
1 Call:
f(7,_475) ?
2 Call: 7<3 ?
2 Fail: 7<3 ?
2 Call: 7<6 ?
2 Fail: 7<6 ?
1 Exit: f(7,2) ?
N = 2 ?
yes
2
2
3
3
1
| ?- f(1,Y).
1
1 Call:
2
2 Call:
2
2 Exit:
1
1 Exit:
Y = 0 ? ;
1
1 Redo:
3
2 Call:
3
2 Exit:
1
1 Exit:
Y = 1 ? ;
1
1 Redo:
1
1 Exit:
Y = 2 ?
yes
f(1,_475) ?
1<3 ?
1<3 ? ?
f(1,0) ?
f(1,0) ?
1<6 ?
1<6 ? ?
f(1,1) ?
f(1,1) ?
f(1,2) ?
Cut の使用法
危険な cut は述語の論理的動作を変える.
危険なcut の使用は避けよ!
危険なcutの使用は, プログラムを読みにくくし, 節の特定の並び
方に依存することになる(データベースに書き込むことになると,
変わってしまう可能性がある).
プログラムの実行効率を上げたいなら, 安全なcut を用いて
backtrack を制御すべきである.
(ある節を選ぶかどうかの)テストの代わりに cut を使うのは避
けるべき.
論理を分かりやすくするには, cut は:
p(X):- test1(X), !, call1(X).
p(X):- test2(X), !, call2(X).
p(X):- testN(X), !, callN(X).
p(1,X):- !, call1(X).
p(2,X):- !, call2(X).
p(3,X):- !, callN(X).
testI 述語は互いに排他的だとする.
互いに排他的なテストは, 節の
頭部に入れた.
Cut - fail
あるゴールが成功する条件を記述したいのと同様に, ゴール
が fail するときを記述したいことがある.
これには, 組み込み述語の fail を cut とを組み合わせて次
のようにする: “ !, fail. “
= この点に到達すれば, 他の節の如何に関わらず, fail する.
(例) 事実 ‘Mary likes all animals except snakes’ を表現したい
ときには
likes(mary,X):snake(X), !, fail.
likes(mary,X):\+ snake(X),
animal(X).
cut と fail を組み合わせることに
より、backtrack 時に発生する,
第2の節への redundant な call
を削除することがてきる.
Cut – fail: なぜ?
しかしながら, cut-fail を用いると, プログラムコードは
読みにくくなる.
ある事実が偽となる条件を記述するより, 真となる条
件を記述する方が, 一般には, 明確で簡単である.
likes(mary,X):\+ snake(X),
animal(X).
当該事実を表すにはこれで充分.
しかしながら, 時には, 真であることより偽であることを
記述する方が簡単であることがあり, そのような時には
cut-fail を用いればプログラムの効率は向上する.
どのcutにも言えることだが; 注意して使え.
まとめ
等号: =, is, =:=, =\=, ==, \==, \+
REDO 対 CALL
Backtrack を制御する: cut !



効率: (成功しえない)不要な REDO の削除.
プログラムの単純化: 節を選択する条件の記述が簡単
になる.
述語のロバスト化: REDO が強制された時でも正当な
動作を行う.
安全な cut = cut は述語論理を変えない = good
危険な cut = cut なくせば論理が変わる = bad
Cut – fail: 偽であることを証明することが真であること
を証明するより楽な場合.
補遺: プログラム例
簡単な数値計算
探索


Knapsack
8-queens
項は評価せず
項はさまざまに書けるが, 評価(計算)はなされな
い
以下は, Prolog では同じ項:
+(1,*(2,3))
1+ *(2,3)
+(1,2*3)
(1+(2*3))
1+2*3
これらはいずれも 7 とは unify しない(計算しな
いから)
式を評価(計算)させるには
?- X is 1+2*3.
X = 7
Yes
組み込み述語 is を用いると, 数式の評価ができ
る
is(X,Y) (X is Y と同じ)は項 Y を評価しそ
の結果のアトムを X に unify する
インスタンス化済みが必要条件
?- Y=X+2, X=1.
Y = 1+2
X = 1
Yes
?- Y is X+2, X=1.
ERROR: Arguments are not sufficiently instantiated
?- X=1, Y is X+2.
X = 1
Y = 3
Yes
評価可能な述語
“ X is Y ” が正しく動くためには, Y に現れるk
述語は評価可能述語 evaluable predicates でな
いといけない
評価可能述語の例: +, -, * , /
組み込みの評価可能述語はその他にもある:
abs(Z) , sqrt(Z) など
整数と浮動小数点数
?- X is
X = 0.5
Yes
?- X is
X = 0.5
Yes
?- X is
X = 2
Yes
?- X is
X = 2
Yes
1/2.
2種類の数値がある: 整数と
不動小数点数.
1.0/2.0.
評価可能述語は上記2つの
全ての組合せに対し
overload されている(同じ名
前で型が異なっても大丈夫).
2/1.
2.0/1.0.
Prolog は動的型付け言語;
型は実行時に用いられて
overload の解消をする.
ゴール 2=2.0 は fail する.
比較
数値の比較演算子:
<, >, =<, >=, =:=, =\=
数値比較のゴールを解決するために, Prolog は
まず両辺を評価し, その結果を数値的に比較す
る
従って、両辺の変数はすべて束縛されていなけ
ればならない
比較
?- 1+2 < 1*2.
No
?- 1<2.
Yes
?- 1+2>=1+3.
No
?- X is 1-3, Y is 0-2, X =:= Y.
X = -2
Y = -2
Yes
Prolog における等号
関係はあるが異なる等価比較演算子:



X is Y は Y を評価し, 結果を X とunifyする: 3 is
1+2 は成功するが, 1+2 is 3 は fail する
X = Y は X と Y をunifyする. 評価はしない: 3 = 1+2
と 1+2 = 3 はともに fail.
X =:= Y は二つを評価し比較する:
3 =:= 1+2 と 1+2 =:= 3 はともに成功する
例: mylength
mylength([],0).
mylength([_|Tail], Len) :mylength(Tail, TailLen),
Len is TailLen + 1.
?- mylength([a,b,c],X).
X = 3
Yes
?- mylength(X,3).
X = [_G266, _G269, _G272]
Yes
例とならない例: mylength
mylength([],0).
mylength([_|Tail], Len) :mylength(Tail, TailLen),
Len = TailLen + 1.
?- mylength([1,2,3,4,5],X).
X = 0+1+1+1+1+1
Yes
例: sum
sum([],0).
sum([Head|Tail],X) :sum(Tail,TailSum),
X is Head + TailSum.
?- sum([1,2,3],X).
X = 6
Yes
?- sum([1,2.5,3],X).
X = 6.5
Yes
例: gcd
gcd(X,Y,Z) :X =:= Y,
Z is X.
gcd(X,Y,Denom) :X < Y,
NewY is Y - X,
gcd(X,NewY,Denom).
gcd(X,Y,Denom) :X > Y,
NewX is X - Y,
gcd(NewX,Y,Denom).
注: ここは次の形ではだめ
gcd(X,X,X)
gcd 述語を動かしてみる
?- gcd(5,5,X).
X = 5
Yes
?- gcd(12,21,X).
X = 3
Yes
?- gcd(91,105,X).
X = 7
Yes
?- gcd(91,X,7).
ERROR: Arguments are not sufficiently instantiated
例: factorial
factorial(X,1) :X =:= 1.
factorial(X,Fact) :X > 1,
NewX is X - 1,
factorial(NewX,NF),
Fact is X * NF.
?- factorial(5,X).
X = 120
Yes
?- factorial(20,X).
X = 2.4329e+018
Yes
?- factorial(-2,X).
No
補遺: プログラム例
簡単な数値計算
探索


Knapsack
8-queens
探索
Prolog の強みは(当然)数値計算ではない
最も得意とするのは, 問題空間内の探索である


問題の論理的な定義を与えれば
Prolog が解を見つけてくれる
(といっても バカ力探索ですが)
Knapsack 問題
キャンプ用の荷物を詰めている
収納庫には次のものがある:
品目
重さ(kg)
カロリー
パン
4
9200
パスタ
2
4600
バター
1
6700
ベビーフード
3
6900
ナップザックには 4 kg 入る.
どう得られべば 4 kg でカロリー最大となるか.
Greedy 法はうまくいかない
品目
重さ(kg)
カロリー
パン
4
9200
パスタ
2
4600
バター
1
6700
ベビーフード
3
6900
最大カロリー優先: パンだけ, 9200
最軽優先: バター + パスタ, 11300
(最適選択: バター + ベビーフード, 13600)
探索
この問題に対するどんなアルゴリズムも次の2条
件を満足することはできないことが知られている


常に最良の答えを出し
指数時間未満で動作する
従って、力ずく探索を使っても、まあ、恥ずかしい
ことではない
これは Prolog にとってはありがたい. 探索は
Prolog が得意とするところである
さて、表現は?
個々の食料品(のデータ)を項 food(N,W,C)
で表そう
収納庫は
[food(bread,4,9200),
food(pasta,2,4500),
food(peanutButter,1,6700),
food(babyFood,3,6900)]
ナップザックの中身も同じ表現としよう
/*
weight(L,N) は食料のリスト L を入力とし、その重さの合計
を求める. なお各食料は food(Name,Weight,Calories) の形
をした項である. 結果は N にunifyする.
*/
weight([],0).
weight([food(_,W,_) | Rest], X) :weight(Rest,RestW),
X is W + RestW.
/*
calories(L,N) は食料の項のリスト L を入力とし, そのカロリー
の合計を求める. なお各食料は food(Name,Weight,Calories)
の形をした項とする. 結果は N にunifyする.
*/
calories([],0).
calories([food(_,_,C) | Rest], X) :calories(Rest,RestC),
X is C + RestC.
/*
subseq(X,Y) が成功するのは, リスト X が, リスト Y の要素から
0個以上の要素を省いたリスとと同じになるとき.
どんなインスタンス化の状態でも動作する.
*/
subseq([],[]).
subseq([Item | RestX], [Item | RestY]) :subseq(RestX,RestY).
subseq(X, [_ | RestY]) :subseq(X,RestY).
リストの subsequence は当該リストの要素から任
意個数要素を取り除いたもののコピーである
(ナップザックの中身は, 収納庫内の中味の
subsequence である)
?- subseq([1,3],[1,2,3,4]).
Yes
?- subseq(X,[1,2,3]).
X
X
X
X
X
X
X
X
=
=
=
=
=
=
=
=
No
[1, 2,
[1, 2]
[1, 3]
[1] ;
[2, 3]
[2] ;
[3] ;
[] ;
3] ;
;
;
;
注: subseq はあるリストが他の
リストの部分列であるかどうかを
テストするだけではない; 部分列
を生成することができる! これを
用いて、ナップザック問題をとく
/*
knapsackDecision(Pantry,Capacity,Goal,Knapsack) の入力
は食料項のリスト Pantry, 正の数 Capacity, 正の数 Goal.
Knapsack は Pantry の部分列で全カロリーが Goal 以上, かつ
総重量が Capacity 以下という制約を満たすものと unify する
*/
knapsackDecision(Pantry,Capacity,Goal,Knapsack) :subseq(Knapsack,Pantry),
weight(Knapsack,Weight),
Weight =< Capacity,
calories(Knapsack,Calories),
Calories >= Goal.
?- knapsackDecision(
|
[food(bread,4,9200),
|
food(pasta,2,4500),
|
food(peanutButter,1,6700),
|
food(babyFood,3,6900)],
|
4,
|
10000,
|
X).
X = [food(pasta, 2, 4500),
food(peanutButter, 1, 6700)]
Yes
これは、与えられたカロリーのゴールに合う解を
(もしあれば)提示してくれる
ということは、求めているものと少々違う、、、、
決定と最適化
先ほど解いたのはナップザック決定問題
decision problem
解きたいのはナップザック最適化問題
optimization problem
これを行うにため, 組み込み述語 findall を
用いる
findall 述語
findall(X,Goal,L)



Goal を証明する全ての方法(X で表現)をみつける
それぞれの証明について, Goal 証明可能インスタン
スとする代入を X に適用する
Unifies L を, 上記のような X のリストと unify する
解を数える
?- findall(1,subseq(_,[1,2]),L).
L = [1, 1, 1, 1]
Yes
これは, subseq(_,[1,2])に4通りの証明方
法があることを示している
一証明につき1個の1をリストとする
解インスタンスを集める
?- findall(subseq(X,[1,2]),subseq(X,[1,2]),L).
X = _G396
L = [subseq([1, 2], [1, 2]), subseq([1], [1, 2]),
subseq([2], [1, 2]), subseq([], [1, 2])]
Yes
findall の第1、第2パラメータは同じ
これにより、ゴール subseq(X,[1,2]) の全4
個の証明可能インスタンスが集まる
特定の代入を集める
?- findall(X,subseq(X,[1,2]),L).
X = _G312
L = [[1, 2], [1], [2], []]
Yes
findall の普通の用法: 第1パラメータは第2
パラメータに含まれる変数
これにより、ゴールsubseq(X,[1,2]) を証明
可能とする, 4個の X を集める
/*
legalKnapsack(Pantry,Capacity,Knapsack)の入力
は食料項のリスト Pantry, 正の数 Capacity, 正の数 Goal.
Knapsack は Pantry の部分列で
総重量が Capacity 以下という制約を満たすものと unify する.
*/
legalKnapsack(Pantry,Capacity,Knapsack):subseq(Knapsack,Pantry),
weight(Knapsack,W),
W =< Capacity.
/*
maxCalories(List,Result) は食料項のリストを入力とする. Result
と unify するのは総カロリーを最大化するリスト要素である. そのために
補助述語 maxC を用意する. これには4個のパラメータがある: 食料リスト
の残りのリスト, これまでみた食料項目のリストで最良のもの,
その総カロリー, 最終結果である
*/
maxC([],Sofar,_,Sofar).
maxC([First | Rest],_,MC,Result) :calories(First,FirstC),
MC =< FirstC,
maxC(Rest,First,FirstC,Result).
maxC([First | Rest],Sofar,MC,Result) :calories(First,FirstC),
MC > FirstC,
maxC(Rest,Sofar,MC,Result).
maxCalories([First | Rest],Result) :calories(First,FirstC),
maxC(Rest,First,FirstC,Result).
/*
knapsackOptimization(Pantry,Capacity,Knapsack)の入力
は食料項のリスト Pantry, 正の数 Capacity, 正の数 Goal.
Knapsack は Pantry の部分列で, 総カロリーが最大, かつ
総重量が Capacity 以下という制約を満たすものと unify する.
*/
knapsackOptimization(Pantry,Capacity,Knapsack) :findall(K,legalKnapsack(Pantry,Capacity,K),L),
maxCalories(L,Knapsack).
?- knapsackOptimization(
|
[food(bread,4,9200),
|
food(pasta,2,4500),
|
food(peanutButter,1,6700),
|
food(babyFood,3,6900)],
|
4,
|
Knapsack).
Knapsack = [food(peanutButter, 1, 6700),
food(babyFood, 3, 6900)]
Yes
補遺: プログラム例
簡単な数値計算
探索


Knapsack
8-queens
The 8-Queens Problem
チェスに関する知識:



8 x 8 の盤面を使用
Queen は垂直, 水平, 対角線方向に何枡でも動ける
2つの queen がチェックの位置にあるというのは, 両方
が同一の水平線上, 垂直線上, または対角線上に
あって, 相手の升目に移動できる状態をいう
問題: 8個の queen を、互いに相手を check しな
いような位置に(queen 以外はない盤面上に) 配
置しなさい
表現は?
第2列・第5行にクィーンがあることを
queen(2,5) で表す
もっとコンパクトかつ読みやすい方法はないか
他の駒はないのであるから—例えば
pawn(X,Y) はないし king(X,Y)もない—
X/Y という形の項を用いることにしよう
(注: (数値計算の) 商だと思って評価することは
ない)
例
盤面はクィーンのリストで表現できる
右下の盤面は [2/5,3/7,6/1]
8
7
Q
6
5
Q
4
3
2
Q
1
1
2
3
4
5
6
7
8
/*
nocheck(X/Y,L) の入力は, クィーン X/Y とクィーンのリスト
である. 成功するのは、 X/Y クィーンがチェックに入らないとき
に限る.
*/
nocheck(_, []).
nocheck(X/Y, [X1/Y1 | Rest]) :X =\= X1,
Y =\= Y1,
abs(Y1-Y) =\= abs(X1-X),
nocheck(X/Y, Rest).
/*
legal(L) が成功するのは L がクィーンの正しい置き方になって
いるとき: すなわち全てのクィーンの座標は所定の範囲内にあり
どのクィーンもチェック状態にない.
*/
legal([]).
legal([X/Y | Rest]) :legal(Rest),
member(X,[1,2,3,4,5,6,7,8]),
member(Y,[1,2,3,4,5,6,7,8]),
nocheck(X/Y, Rest).
そこそこ
これはもう充分な解: 質問 legal(X) に対し、
正しい配置が全て求まる:
?- legal(X).
X = [] ;
X = [1/1] ;
X = [1/2] ;
X = [1/3]
8-Queen 問題の解
勿論、長い時間がかかる: 一個のクィーンがおけ
る 64個の解を見つけ、それを元に、2個のクィー
ンの解、次には3個のクィーンの解と求めていく
(そうではなく)すぐに 8-queens 問題 にすぐ取り
掛かるように, 別の query を考える:
?- X = [_,_,_,_,_,_,_,_], legal(X).
X = [8/4, 7/2, 6/7, 5/3, 4/6, 3/8, 2/5, 1/1]
Yes
例
8-queen 問題のある解
[8/4, 7/2, 6/7, 5/3,
4/6, 3/8, 2/5, 1/1]
8
Q
7
Q
6
Q
5
Q
Q
4
Q
3
Q
2
1
Q
1
2
3
4
5
6
7
8
改善の余地あり
遅い
次から次へとトリビアルな置換を見つけている:
?- X = [_,_,_,_,_,_,_,_], legal(X).
X = [8/4, 7/2, 6/7, 5/3, 4/6, 3/8, 2/5, 1/1] ;
X = [7/2, 8/4, 6/7, 5/3, 4/6, 3/8, 2/5, 1/1] ;
X = [8/4, 6/7, 7/2, 5/3, 4/6, 3/8, 2/5, 1/1] ;
X = [6/7, 8/4, 7/2, 5/3, 4/6, 3/8, 2/5, 1/1]
改善
明らかに、どの解も各列に一個のクィーンがある
従って、どの解もある固定した順に書くことがで
きる:
X=[1/_,2/_,3/_,4/_,5/_,6/_,7/_,8/_]
この形のゴール項から開始することにより探索を
制限する(従って、探索速度を上げる)ことができ、
trivial な順列を抑止することができる
/*
eightqueens(X) が成功するのは X が8個のクィーンの
正しいおき方であって, X 座標の順にリストされているときのみ
である.
*/
eightqueens(X) :X = [1/_,2/_,3/_,4/_,5/_,6/_,7/_,8/_],
legal(X).
nocheck(_, []).
nocheck(X/Y, [X1/Y1 | Rest]) :% X =\= X1, X達は互いに異なると仮定
Y =\= Y1,
abs(Y1-Y) =\= abs(X1-X),
nocheck(X/Y, Rest).
legal([]).
legal([X/Y | Rest]) :legal(Rest),
% member(X,[1,2,3,4,5,6,7,8]), X は領域内と仮定
member(Y,[1,2,3,4,5,6,7,8]),
nocheck(X/Y, Rest).
全 X-座標はすでに範囲内にあり異なることが分
かっているので, 少し最適化に寄与する
改良版 8-Queen 問題解
速くなった
煩わしい順列がない
?- eightqueens(X).
X = [1/4, 2/2, 3/7, 4/3, 5/6, 6/8, 7/5, 8/1] ;
X = [1/5, 2/2, 3/4, 4/7, 5/3, 6/8, 7/6, 8/1]
実験
legal([]).
legal([X/Y | Rest]) :legal(Rest),
% member(X,[1,2,3,4,5,6,7,8]), X は範囲内と仮定
1=<Y, Y=<8, % 旧 member(Y,[1,2,3,4,5,6,7,8]),
nocheck(X/Y, Rest).
Fail する: “arguments not sufficiently instantiated”
この member による条件は座標が範囲内にある
かの単なるテスト test ではない; それを生成
generate している
実験もう一つ
legal([]).
legal([X/Y | Rest]) :% member(X,[1,2,3,4,5,6,7,8]), X は範囲内と仮定
member(Y,[1,2,3,4,5,6,7,8]),
nocheck(X/Y, Rest),
legal(Rest). % もとは最初の条件
Fail する: “arguments not sufficiently instantiated”
legal(Rest) の条件は最初にくるべき, という
のも, これが部分解を生成 generate して、それが
nocheck でテストされるから
The Farmer-Wolf-Goat-Cabbage Problem
要素: farmer,wolf,goat and the cabbage.
川を渡らなければいけない. 制約がある.
•
•
•
ボートは一度に2個(人)しか運べない
wolf と goat だけを一緒にすることはできない
goat と cabbage だけを一緒にすることはできない
川の対岸へ、上記要素を全て運ぶ手順を考えよ.