我有以下prolog程序:
square([H|T], X) :-
squareCompare(T, H, X).
squareCompare([], X, X * X ).
squareCompare([H|T], V, Result) :-
(V * V) < (H * H),
squareCompare(T, V, Result);
(V * V) > (H * H),
squareCompare(T, H, Result).
当我进入时:
square([7, 5, 2], Result).
我得到Result = 2 * 2
,我想要的是Result = 4
。
该程序搜索列表中元素的最小平方。
答案 0 :(得分:3)
除了评论中指出的缺少算术评估(是/ 2)之外,还有使用<
/ 2和>
/ 2的问题:您的谓词不适用于列表连续重复,例如:
?- square([7,7],X).
false.
预期结果为49.您可以通过<
/ 2替换=<
/ 2或>
/ 2替换>=
/ 2来解决这个问题。 squareCompare / 3的递归规则:
squareCompare([], X, Y) :-
Y is X*X.
squareCompare([H|T], V, Result) :-
(V * V) < (H * H),
squareCompare(T, V, Result);
(V * V) >= (H * H),
squareCompare(T, H, Result).
现在谓词产生了所需的结果:
?- square([7,7],X).
X = 49.
根据评论中的另一个建议,您可以选择使用CLP(FD)使谓词双向工作。在这种情况下,谓词类似于真实的关系,所以给它一个反映这个事实的更具描述性的名称是合适的,比如list_minsquare / 2。既然你对最小的方块感兴趣,为什么不绕着正方形作为参数而不是数字呢?最坏的情况:最小正方形的根是最后一个列表元素,然后没有区别。最佳情况:最小正方形的根是第一个列表元素,然后您只计算一次而不是列表长度时间。把所有这些放在一起:
:- use_module(library(clpfd)).
list_minsquare([H|T],X) :-
S #= H*H,
list_square_minsquare(T,S,X).
list_square_minsquare([],S,S).
list_square_minsquare([H|T],S,Result) :-
S #< (H*H),
list_square_minsquare(T,S,Result).
list_square_minsquare([H|T],S,Result) :-
H2 #= (H*H),
S #>= H2,
list_square_minsquare(T,H2,Result).
现在让我们看一些动作。您的示例查询会产生所需的结果:
?- list_minsquare([7,4,2],X).
X = 4.
连续重复也不会造成麻烦:
?- list_minsquare([7,7],X).
X = 49.
部分实例化列表导致生成所有可能的解决方案:
?- list_minsquare([7,Y,2],X).
X = 4, % <- 1st answer: X=4 if
Y^2#=_G670,
_G670 in 50..sup ; % Y^2 is between 50 and sup
Y in -1..1, % <- 2nd answer: if Y in -1..1
Y^2#=X, % then X=Y^2
X in 0..1 ;
X = 4, % <- 3rd answer: X=4
Y in -7.. -1\/1..7, % if Y in -7..-1 or 1..7
Y^2#=_G1754,
_G1754 in 4..49. % and Y^2 in 4..49
在上面的示例中,Y
有三种可能性,其中没有一种具有唯一的解决方案,因此您可以在答案中获得剩余目标。如果您希望得到具体的解决方案,您可以约束Y
的范围,并询问带有标签/ 1的具体数字:
?- Y in 0..3, list_minsquare([7,Y,2],X), label([Y]).
Y = X, X = 0 ;
Y = X, X = 1 ;
Y = 2,
X = 4 ;
Y = 3,
X = 4.
最常见的查询也有效。但是,它以不公平的方式列出了解决方案:
?- list_minsquare(L,X).
L = [_G97], % <- 1st solution
_G97^2#=X,
X in 0..sup ;
L = [_G266, _G269], % <- 2nd solution
_G266^2#=X,
X in 0..sup,
X+1#=_G309,
_G309 in 1..sup,
_G332#>=_G309,
_G332 in 1..sup,
_G269^2#=_G332 ;
L = [_G494, _G497, _G500], % <- 3rd solution
_G494^2#=X,
X in 0..sup,
X+1#=_G540,
X+1#=_G552,
_G540 in 1..sup,
_G575#>=_G540,
_G575 in 1..sup,
_G500^2#=_G575,
_G552 in 1..sup,
_G620#>=_G552,
_G620 in 1..sup,
_G497^2#=_G620 ;
.
.
.
在进入下一个长度之前,您只能获得每个列表长度的一个解决方案。您可以通过在查询中添加目标长度/ 2来获得公平的排序。然后,您将获得每个列表长度的所有可能性,然后继续:
?- length(L,_), list_minsquare(L,X).
L = [_G339], % <- 1st solution: list with one element
_G339^2#=X,
X in 0..sup ;
L = [_G1036, _G1039], % <- 2nd solution: list with two elements
_G1036^2#=X, % X is square of 1st element
X in 0..sup,
X+1#=_G1079,
_G1079 in 1..sup,
_G1102#>=_G1079,
_G1102 in 1..sup,
_G1039^2#=_G1102 ;
L = [_G935, _G938], % <- 3rd solution: list with two elements
_G935^2#=_G954,
_G954 in 0..sup,
_G954#>=X,
X in 0..sup,
_G938^2#=X ; % X is square of 2nd element
.
.
.
当然,你也可以在上面的查询列表中约束和标记数字,你仍然可以在无限多的解决方案中得到具体数字(因为列表长度无限多)。
?- length(L,_), L ins 1..2, list_minsquare(L,X), label(L).
L = [1],
X = 1 ;
L = [2],
X = 4 ;
L = [1, 2],
X = 1 ;
L = [1, 1],
X = 1 ;
L = [2, 1],
X = 1 ;
L = [2, 2],
X = 4 ;
L = [1, 2, 2],
X = 1 ;
L = [1, 2, 1],
X = 1 ;
L = [1, 1, 2],
X = 1 ;
L = [2, 1, 2],
X = 1 ;
.
.
.