在 Prolog 中实例化整数变量

Instantiating integer variables in Prolog

作为我的第一个 Prolog 挑战,我正在尝试解决这个难题:

There are eight floors in an apartment building. The fifth floor has the only apartment with two bedrooms. Mrs Barber has a baby and cannot carry a pram upstairs when the lift is out of order. Mr and Mrs Elder also find climbing difficult now that they’re retired. Mr Archer likes the peace and quiet of living on the top floor. Mrs Cook and her daughter need a two bedroom flat. Mr and Mrs Hooper live just below Mr Archer. Mrs Cook lives above Mr Gardner and below Mrs Driver. Mr and Mrs Fisher live above Mr and Mrs Elder.

本质上,我将其视为一组代数整数关系,因此我想在 [1..8] 中将每个实体实例化为一个整数,并声明已知的值和关系。

floors([Archer,Barber,Cook,Driver,Elder,Fisher,Gardner,Hooper]) :-
    Archer=8,
    Cook=5,
    Hooper is Archer-1,
    Barber<4,
    Elder<4,
    Fisher>Elder,
    Cook>Gardner,
    Cook<Driver.
?- floors([A,B,C,D,E,F,G,H]).

但是我在第 5 行得到 ERROR:Arguments are not sufficiently instantiated(第一个不等式)。

我试过添加

Vars = [Archer,Barber,Cook,Driver,Elder,Fisher,Gardner,Hooper],
Vars ins 1..8,

但后来我得到了模糊的 ERROR: Unknown procedure: floors/1 (DWIM could not correct goal)

我确定我遗漏了一些明显的东西。感谢所有帮助。

我假设使用整数而不是 FloorOneFloorTwo 等会更简单。然而,在 Prolog 中对整数进行推理需要一个名为 CLP(FD)(或 CLP(ℤ))的特殊库,它代表 有限域上的约束逻辑编程,其工作方式与事实推理的基本案例。 (感谢 false for the tip - for more on this see here。)在 SWI-Prolog 中,必须显式导入。在我的例子中 space 的可能性是居民和楼层数的所有排列,事实限制了允许排列的 space。

% import a constraint-solving library
% note that the syntax for clpfd has a hash sign (#=, #<, etc)
:- use_module(library(clpfd)).

floors([Archer,Barber,Cook,Driver,Elder,Fisher,Gardner,Hooper]) :-
    % possible facts are all the permutations
  permutation( [Archer, Barber, Cook, Driver, Elder, Fisher, Gardner, Hooper], [1, 2, 3, 4, 5, 6, 7, 8]),
    % clues
  Archer #= 8,
  Cook #= 5,
  Hooper is Archer - 1,
  Barber #= 1,
  Elder #= 2,
  Fisher #> Elder,
  Cook #> Gardner,
  Cook #< Driver.

?- floors([A,B,C,D,E,F,G,H]).
A = 8,
B = 1,
C = 5,
D = 6,
E = 2,
F = 3,
G = 4,
H = 7 ;
A = 8,
B = 1,
C = 5,
D = 6,
E = 2,
F = 4,
G = 3,
H = 7

您的原始代码中出现错误消息的原因如下:当您在 Barber<4 中使用像 < 这样的运算符时,变量 Barber 必须在之前实例化. (想想替代方案,即 prolog 应该为 Barber 选择一个值以满足目标 'Barber<4':实例化 Barber 的可能性的数量将是巨大的。)没有目标,然而,在 Barber<4 之前强制序言实例化 Barber.

您已通过添加目标

解决了您自己对问题的回答中的问题
permutation([Archer, Barber, Cook, Driver, Elder, Fisher, Gardner, Hooper], [1, 2, 3, 4, 5, 6, 7, 8])

因为这个目标强制每个变量 Archer .. Hooper 用其中一个数字实例化。使用这种方法,您可以只使用不带“#”的原始运算符,因为将不再发出有关未充分实例化变量的原始错误消息。

在实例化整数变量时,还有另一个谓词可能对您有所帮助:between/3,可以在您的代码中按如下方式使用:... between(1,8,Barber), Barber<4, .. . .目标 between(1,8,Barber) 将确保 Barber 在考虑目标 Barber<4 之前使用来自 1..8 的值实例化。

当然,在这种特殊情况下,您可以将两个目标 between(1,8,Barber), Barber<4 合并为一个目标 between(1,3,Barber)。因此,完整的示例也可以用 between/3 而不是 permutation/2 以下列方式实现:

:- use_module(library(clpfd)).
floors([Archer,Barber,Cook,Driver,Elder,Fisher,Gardner,Hooper]) :-
    Archer=8,
    Cook=5,
    Hooper is Archer-1,
    between(1,3,Barber),
    between(1,3,Elder),
    AboveElder is Elder+1,
    between(AboveElder,8,Fisher),
    BelowCook is Cook-1,
    between(1,BelowCook,Gardner),
    AboveCook is Cook+1,
    between(AboveCook,8,Driver),
    all_different([Archer,Barber,Cook,Driver,Elder,Fisher,Gardner,Hooper]).

我喜欢从结构上看这类谜题。我会从这个开始:

[floor(1,[_]),floor(2,[_]),floor(3,[_]),floor(4,[_]),floor(5,[_,_]),floor(6,[_]),floor(7,[_]),floor(8,[_])]

请注意,我使用了一个匿名变量列表来表示每层楼的卧室。 5楼是唯一一个有两间卧室的。

现在我需要定义一堆辅助谓词。

onfloor(N,X,Floors) :-
    member(floor(N,Xs),Floors),
    member(X,Xs).

justbelow(X,Y,[floor(_,Xs),floor(_,Ys)|_]) :-
    member(X,Xs),
    member(Y,Ys).
    
justbelow(X,Y,[_|T]) :-
    justbelow(X,Y,T).
    
below(X,Y,Floors) :-
    append(Lower,Upper,Floors),
    onfloor(_,X,Lower),
    onfloor(_,Y,Upper). 

lower3([A,B,C|_],[A,B,C]).

top([T],[T]).
top([_|T],X) :- top(T,X).

现在我的 sovle(Floors) 谓词是每个线索的简单翻译:

solve(Floors) :-
    Floors = [floor(1,[_]),floor(2,[_]),floor(3,[_]),floor(4,[_]),floor(5,[_,_]),floor(6,[_]),floor(7,[_]),floor(8,[_])],
    lower3(Floors,Lower),
    onfloor(_,barber,Lower),
    onfloor(_,elder,Lower),
    top(Floors,Top),
    onfloor(_,archer,Top),
    onfloor(Floor,cook,Floors),
    onfloor(Floor,daughter,Floors),
    justbelow(hooper,archer,Floors),
    below(gardener,cook,Floors),
    below(cook,driver,Floors),
    below(elder,fisher,Floors),
    true.

当我 运行 使用 ?- solve(Floors). 时,我得到以下信息:

floor(1,[barber]),
floor(2,[elder]),
floor(3,[gardener]),
floor(4,[fisher]),
floor(5,[cook,daughter]),
floor(6,[driver]),
floor(7,[hooper]),
floor(8,[archer])