6

選択した制約ソルバーを使用してゼブラパズルを解く演習を行い、Prologclpfdライブラリを使用して試しました。

Prologでこの問題を解決する他の慣用的な方法があることを私は知っていますが、この質問は特にclpfdパッケージに関するものです!

だから私が解決しようとしているパズルの特定のバリエーション(それらがたくさんあるとすると)はこれです:

5軒の家があります

  1. イギリス人は赤い家に住んでいます
  2. スウェーデン人は犬を飼っています
  3. デンマーク人はお茶を飲むのが好きです
  4. 緑の家は白い家に残されています
  5. 温室の所有者はコーヒーを飲みます
  6. ポールモールを吸う人は鳥を飼っています
  7. 真ん中の家でミルクを飲む
  8. 黄色い家の所有者はダンヒルを吸う
  9. ノルウェー人は最初の家に住んでいます
  10. マールボロ喫煙者は猫の飼い主の隣に住んでいます
  11. 馬の飼い主はダンヒルを吸う人の隣に住んでいます
  12. ウィンフィールド喫煙者はビールを飲むのが好きです
  13. ノルウェー人は青い家の隣に住んでいます
  14. ドイツ人はロスマンを吸う
  15. マールボロ喫煙者には、水を飲む隣人がいます

私は次のアプローチでそれを解決しようとしました:

家が持つことができる各属性は、「英国」、「犬」、「緑」などの変数としてモデル化されます。属性は、それらが発生する家に応じて、たとえば変数の場合、1〜5の値を取ることができます。 「犬」は値3を取り、犬は3番目の家に住んでいます。

このアプローチにより、次のような隣接制約のモデル化が容易になります。

def neighbor(X, Y) :-
    (X #= Y-1) #\/ (X #= Y+1).

しかし、どういうわけか、clpfd(IMO)問題が正しくモデル化されていても、パッケージは解決策を生成しません(Choco制約ソルバーでまったく同じモデルを使用し、結果は正しいものでした)。

完全なコードは次のとおりです。

:- use_module(library(clpfd)).

neighbor(X, Y) :-
    (X #= (Y - 1)) #\/ (X #= (Y + 1)).

solve([British, Swedish, Danish, Norwegian, German], Fish) :-

    Nationalities = [British, Swedish, Danish, Norwegian, German],
    Colors = [Red, Green, Blue, White, Yellow],
    Beverages = [Tea, Coffee, Milk, Beer, Water],
    Cigarettes = [PallMall, Marlboro, Dunhill, Winfield, Rothmanns],
    Pets = [Dog, Bird, Cat, Horse, Fish],

    all_different(Nationalities),
    all_different(Colors),
    all_different(Beverages),
    all_different(Cigarettes),
    all_different(Pets),

    Nationalities ins 1..5,
    Colors ins 1..5,
    Beverages ins 1..5,
    Cigarettes ins 1..5,
    Pets ins 1..5,

    British #= Red, % Hint 1
    Swedish #= Dog, % Hint 2
    Danish #= Tea, % Hint 3

    Green #= White - 1 , % Hint 4
    Green #= Coffee, % Hint 5,
    PallMall #= Bird, % Hint 6

    Milk #= 3, % Hint 7
    Yellow #= Dunhill, % Hint 8,
    Norwegian #= 1, % Hint 9

    neighbor(Marlboro, Cat), % Hint 10
    neighbor(Horse, Dunhill), % Hint 11
    Winfield #= Beer, % Hint 12

    neighbor(Norwegian, Blue), % Hint 13
    German #= Rothmanns, % Hint 14,
    neighbor(Marlboro, Water). % Hint 15

内の概念を誤解しましたかclpfd、それともここで明らかな何かを単に見逃していますか?それが役立つ場合は、ここでChocoとScalaを使用して実装された同じアプローチを見つけることができます。


編集:ソルバーが問題を解決できないと私が信じる理由は、変数の明確な値が出てこないためですが、「魚1..3 \/5」などの範囲のみが出てくるからです。

4

2 に答える 2

6

ここにはいくつかの誤解があります。「clpfdパッケージは解決策を生み出さない」と述べていますが、実際には解決策を生み出します。

?- solve(Ls, Fish), label(Ls).
Ls = [3, 5, 2, 1, 4],
Fish in 1\/4,
all_different([5, 3, _G3699, 2, Fish]),
_G3699 in 1\/4,
_G3699+1#=_G3727,
_G3741+1#=_G3699,
_G3727 in 2\/4..5,
2#=_G3727#<==>_G3766,
_G3766 in 0..1,
_G3792#\/_G3766#<==>1,
_G3792 in 0..1,
2#=_G3741#<==>_G3792,
_G3741 in 0\/2..3.

したがって、解決策がある場合、Fishは1または4のいずれかであることがわかります。1を試してみましょう。

?- solve(Ls, Fish), label(Ls), Fish = 1.
false.

いいえ。では、4を試してみましょう。

?- solve(Ls, Fish), label(Ls), Fish = 4.
Ls = [3, 5, 2, 1, 4],
Fish = 4.

これは機能し、問題の根本的な解決策です。たとえば、ラベル付けする変数にFishを含めるなど、別の方法で取得できます。

?- solve(Ls, Fish), label([Fish|Ls]).
Ls = [3, 5, 2, 1, 4],
Fish = 4 ;
false.

ラベル付けの目的は、実際に解があるかどうかに関係なく、制約された変数の具体的な値を試すことです。偶然にも、all_distinct / 1は、この場合、それ自体で基底解を生成するのに十分強力ですが、一般的にはそうではなく、最終的にはラベル付けを使用して無条件の(つまり、保留中の制約がなくなる)答えを取得する必要があります。もちろん、一般的にはすべてにラベルを付ける必要があります最初に行ったようにそれらのサブセットだけでなく、関心のある変数。単一の変数にラベルを付けるには、indomain / 1を使用できるため、上記の最初のクエリにindomain(Fish)を追加することもできます。上記のように、上記の最も一般的なクエリsolve(X、Y)は、投稿したコードで機能します。最後に、これをチェックしてください:

neighbor(X, Y) :- abs(X-Y) #= 1.
于 2012-06-20T16:31:58.530 に答える
3

SWI-Prologでコードを実行すると、

?- solve(X),label(X).
X = [3, 5, 2, 1, 4].

なしlabel

?- solve(X).
X = [3, _G3351, _G3354, 1, _G3360],
_G3351 in 4..5,
all_different([_G3351, _G3386, _G3389, 2, _G3395]),
all_different([3, _G3351, _G3354, 1, _G3360]),
_G3386 in 3..5,
all_different([_G3386, _G3444, 1, _G3450, _G3360]),
_G3389 in 1\/3..5,
_G3389+1#=_G3478,
_G3492+1#=_G3389,
_G3395 in 1\/3..5,
_G3478 in 2..6,
_G3444#=_G3478#<==>_G3529,
_G3444 in 2..5,
_G3444#=_G3556#<==>_G3553,
_G3444#=_G3568#<==>_G3565,
_G3444#=_G3492#<==>_G3577,
_G3450 in 2\/5,
all_different([_G3354, 4, 3, _G3450, _G3614]),
_G3360 in 2\/4..5,
_G3354 in 2\/5,
_G3614 in 1..2\/5,
_G3614+1#=_G3556,
_G3568+1#=_G3614,
_G3556 in 2..3\/6,
_G3553 in 0..1,
_G3565#\/_G3553#<==>1,
_G3565 in 0..1,
_G3568 in 0..1\/4,
_G3492 in 0..4,
_G3577 in 0..1,
_G3577#\/_G3529#<==>1,
_G3529 in 0..1.

に変更all_differentするall_distinctと、ラベルなしのソリューションが得られます。

....
all_distinct(Nationalities),
all_distinct(Colors),
all_distinct(Beverages),
all_distinct(Cigarettes),
all_distinct(Pets),
....

?- solve(X).
X = [3, 5, 2, 1, 4].

all_distinctご覧のとおり、ドキュメントにはvsのより強力な伝播が記載されていall_differentます。提案されたサンプルを実行すると、それらの違いを理解するのに役立ちます。

?- maplist(in, Vs, [1\/3..4, 1..2\/4, 1..2\/4, 1..3, 1..3, 1..6]), all_distinct(Vs).
false.

?- maplist(in, Vs, [1\/3..4, 1..2\/4, 1..2\/4, 1..3, 1..3, 1..6]), all_different(Vs).
Vs = [_G419, _G422, _G425, _G428, _G431, _G434],
_G419 in 1\/3..4,
all_different([_G419, _G422, _G425, _G428, _G431, _G434]),
_G422 in 1..2\/4,
_G425 in 1..2\/4,
_G428 in 1..3,
_G431 in 1..3,
_G434 in 1..6.
于 2012-06-20T15:46:16.643 に答える