Prolog 序言:若顺序不重要,则确定列表中的元素是否相等
我试图找出一种方法来检查两个列表是否相等,而不管它们的元素顺序如何 我的第一次尝试是:Prolog 序言:若顺序不重要,则确定列表中的元素是否相等,prolog,logical-purity,Prolog,Logical Purity,我试图找出一种方法来检查两个列表是否相等,而不管它们的元素顺序如何 我的第一次尝试是: areq([],[]). areq([],[_|_]). areq([H1|T1], L):- member(H1, L), areq(T1, L). 但是,这仅检查左侧列表的所有元素是否都存在于右侧列表中;意思areq([1,2,3],[1,2,3,4])=>true。在这一点上,我需要找到一种能够在双向意义上测试事物的方法。我的第二次尝试是: areq([],[]). areq([],[_|_]). a
areq([],[]).
areq([],[_|_]).
areq([H1|T1], L):- member(H1, L), areq(T1, L).
但是,这仅检查左侧列表的所有元素是否都存在于右侧列表中;意思areq([1,2,3],[1,2,3,4])=>true
。在这一点上,我需要找到一种能够在双向意义上测试事物的方法。我的第二次尝试是:
areq([],[]).
areq([],[_|_]).
areq([H1|T1], L):- member(H1, L), areq(T1, L), append([H1], T1, U), areq(U, L).
?- equal_elementsB([],Xs), false. % terminates universally
false.
?- equal_elementsB([_],Xs), false. % gives a single answer, but ...
%%% wait forever % ... does not terminate universally
在那里,我会尝试重建左边的最新列表,并最终交换列表;但这次失败得很惨
我对递归的感觉非常差,根本不知道如何改进它,尤其是在Prolog中。在这一点上,任何提示或建议都将不胜感激。在序言中,您通常可以完全按照自己所说的去做
areq([],_).
areq([H1|T1], L):- member(H1, L), areq(T1, L).
bi_areq(L1, L2) :- areq(L1, L2), areq(L2, L1).
必要时重命名。使用
排序/2
ISO标准内置谓词的简单解决方案,假设两个列表都不包含重复的元素:
equal_elements(List1, List2) :-
sort(List1, Sorted1),
sort(List2, Sorted2),
Sorted1 == Sorted2.
一些示例查询:
| ?- equal_elements([1,2,3],[1,2,3,4]).
no
| ?- equal_elements([1,2,3],[3,1,2]).
yes
| ?- equal_elements([a(X),a(Y),a(Z)],[a(1),a(2),a(3)]).
no
| ?- equal_elements([a(X),a(Y),a(Z)],[a(Z),a(X),a(Y)]).
yes
?- areq([],[]).
true.
?- areq([1],[]).
false.
?- areq([],[1]).
false.
?- areq([1,2,3],[3,2,1]).
true.
?- areq([1,1,2,2],[2,1,2,1]).
true.
紧凑的形式:
member_(Ys, X) :- member(X, Ys).
equal_elements(Xs, Xs) :- maplist(member_(Ys), Xs).
但是,使用member/2似乎效率不高,并且为重复项(两侧)留下了模糊的空间。相反,我会使用select/3
?- [user].
equal_elements([], []).
equal_elements([X|Xs], Ys) :-
select(X, Ys, Zs),
equal_elements(Xs, Zs).
^D在这里
1 ?- equal_elements(X, [1,2,3]).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
2 ?- equal_elements([1,2,3,3], [1,2,3]).
false.
或者更好
equal_elements(Xs, Ys) :- permutation(Xs, Ys).
作为一个起点,让我们以@capelical的第二个实现为例:
equal_elements([], []).
equal_elements([X|Xs], Ys) :-
select(X, Ys, Zs),
equal_elements(Xs, Zs).
上述实现为以下查询留下了无用的选择点:
?- equal_elements([1,2,3],[3,2,1]).
true ; % succeeds, but leaves choicepoint
false.
我们能做什么?我们可以通过使用
而不是
,但这样做我们会输!我们能做得更好吗
我们可以强>
介绍selectd/3
,这是一个逻辑纯谓词,结合了selectchk/3
的决定论和select/3
的纯性<代码>选择D/3基于
以及:
selectd/3
可以作为select/3
的插入式替代品,因此使用起来很简单
equal_elementsB([], []).
equal_elementsB([X|Xs], Ys) :-
selectd(X, Ys, Zs),
equal_elementsB(Xs, Zs).
让我们看看它的实际行动吧
?- equal_elementsB([1,2,3],[3,2,1]).
true. % succeeds deterministically
?- equal_elementsB([1,2,3],[A,B,C]), C=3,B=2,A=1.
A = 1, B = 2, C = 3 ; % still logically pure
false.
编辑2015-05-14 如果谓词 应强制要求项目出现在具有 同样的多重性。
equal_element SB/2
是这样的,如以下两个查询所示:
?- equal_elementsB([1,2,3,2,3],[3,3,2,1,2]).
true.
?- equal_elementsB([1,2,3,2,3],[3,3,2,1,2,3]).
false.
让我们像上面一样运行两个查询,这次使用equal\u elementsC/2
:
?- equal_elementsC([1,2,3,2,3],[3,3,2,1,2]).
true.
?- equal_elementsC([1,2,3,2,3],[3,3,2,1,2,3]).
true.
但是,如果我们翻转第一个和第二个参数,我们将得到终止
?- equal_elementsB(Xs,[]), false. % terminates universally
false.
?- equal_elementsB(Xs,[_]), false. % terminates universally
false.
为了检查非终止是否消失,我们将使用两个谓词的查询对头放置:
?- equal_elementsB([_],Xs), false.
%%% wait forever % does not terminate universally
?- equal_elementsBB([_],Xs), false.
false. % terminates universally
?-相等元素sb([[uu],Xs),false。
%%%“永远等待%”不会全部终止
?-相等的元素sbb([[uu],Xs),假。
错.%普遍终止
请注意,相同的“技巧”不适用于equal_elementsC/2
,
因为解集的大小是无限的(除了最琐碎的有趣的例子之外)。其他答案都很优雅(远高于我自己的Prolog水平),但让我吃惊的是,这个问题 对常规使用有效 被接受的答案是O(max(| A | log(| A |)),|B | log(| B |)),,而不管列表是否相等(直到排列) 至少,在费心排序之前检查长度是值得的,这将在列表长度不相等的情况下将运行时间减少到线性 扩展这一点,不难修改解决方案,以便在列表不相等(直至排列)的一般情况下,使用随机变量使其运行时有效地线性化 假设我们定义
digest(L, D) :- digest(L, 1, D).
digest([], D, D) :- !.
digest([H|T], Acc, D) :-
term_hash(H, TH),
NewAcc is mod(Acc * TH, 1610612741),
digest(T, NewAcc, D).
这是数学函数Prod_i h(a_i)|p的Prolog版本,其中h是散列,p是素数。它有效地将每个列表映射到范围0,…,p-1(在上面,p是大素数1610612741)内的随机(在散列意义上)值
现在,我们可以检查两个列表是否具有相同的摘要:
same_digests(A, B) :-
digest(A, DA),
digest(B, DB),
DA =:= DB.
如果两个列表有不同的摘要,它们就不可能相等。如果两个列表有相同的摘要,那么它们不相等的可能性很小,但这仍然需要检查。对于这个案例,我无耻地窃取了Paulo Moura的优秀答案
最后的代码是:
equal_elements(A, B) :-
same_digests(A, B),
sort(A, SortedA),
sort(B, SortedB),
SortedA == SortedB.
same_digests(A, B) :-
digest(A, DA),
digest(B, DB),
DA =:= DB.
digest(L, D) :- digest(L, 1, D).
digest([], D, D) :- !.
digest([H|T], Acc, D) :-
term_hash(H, TH),
NewAcc is mod(Acc * TH, 1610612741),
digest(T, NewAcc, D).
受qsort启发,有一种可能性:
split(_,[],[],[],[]) :- !.
split(X,[H|Q],S,E,G) :-
compare(R,X,H),
split(R,X,[H|Q],S,E,G).
split(<,X,[H|Q],[H|S],E,G) :-
split(X,Q,S,E,G).
split(=,X,[X|Q],S,[X|E],G) :-
split(X,Q,S,E,G).
split(>,X,[H|Q],S,E,[H|G]) :-
split(X,Q,S,E,G).
cmp([],[]).
cmp([H|Q],L2) :-
split(H,Q,S1,E1,G1),
split(H,L2,S2,[H|E1],G2),
cmp(S1,S2),
cmp(G1,G2).
split(u,[],[],[],[],[]):-!。
分裂(X[H | Q],S,E,G):-
比较(R,X,H),
分裂(R,X,[H | Q],S,E,G)。
分裂(,X,[H | Q],S,E,[H | G]):-
分割(X,Q,S,E,G)。
cmp([],[])。
cmp([H | Q],L2):-
拆分(H、Q、S1、E1、G1),
分裂(H,L2,S2,[H | E1],G2),
cmp(S1,S2),
cmp(G1,G2)。
使用cut的简单解决方案
areq(A,A):-!.
areq([A|B],[C|D]):-areq(A,C,D,E),areq(B,E).
areq(A,A,B,B):-!.
areq(A,B,[C|D],[B|E]):-areq(A,C,D,E).
一些示例查询:
| ?- equal_elements([1,2,3],[1,2,3,4]).
no
| ?- equal_elements([1,2,3],[3,1,2]).
yes
| ?- equal_elements([a(X),a(Y),a(Z)],[a(1),a(2),a(3)]).
no
| ?- equal_elements([a(X),a(Y),a(Z)],[a(Z),a(X),a(Y)]).
yes
?- areq([],[]).
true.
?- areq([1],[]).
false.
?- areq([],[1]).
false.
?- areq([1,2,3],[3,2,1]).
true.
?- areq([1,1,2,2],[2,1,2,1]).
true.
您所描述的内容称为.Promise:将为纯解(如@Pyetras)提供奖励。将为确定解和纯解增加(即,进一步的奖励)。谓词
member/2
使用统一,这意味着两个列表可能不同(问题是关于元素相等)当测试它们是否有相等的元素时变得相等。这是一个很好且纯粹的解决方案。唯一的保留是,它留下了开放的选择点,因此不像可能的那样具有确定性。biu areq
函数的目的是什么?我看不到它是从areq调用的。你能进一步解释吗?但是select/3
使用统一。因此,如果测试元素相等性,则与成员/2
存在相同的问题。使用库置换/2
也有相同的问题。更好!但不足以确定!(如果适用)
?- areq([],[]).
true.
?- areq([1],[]).
false.
?- areq([],[1]).
false.
?- areq([1,2,3],[3,2,1]).
true.
?- areq([1,1,2,2],[2,1,2,1]).
true.