我正在试图找出一种方法来检查两个列表是否相等,无论它们的元素顺序如何。
我的第一次尝试是:
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).
我会尝试在左边重建lest,最后交换列表;但这次失败了。
我对递归的感觉非常差,根本不知道如何改进它,尤其是Prolog。任何提示或建议都将在此时受到赞赏。
答案 0 :(得分:9)
使用sort/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
答案 1 :(得分:9)
作为一个起点,让@CapelliC第二次实施equal_elements/2
:
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.
我们能做什么?我们可以通过使用来解决效率问题
selectchk/3
代替
select/3
,但这样做我们会失去logical-purity! 我们可以做得更好吗?
我们可以!
引入selectd/3
,这是一个逻辑上纯的谓词,它结合了selectchk/3
的确定性和select/3
的纯度。 selectd/3
基于
if_/3
和(=)/3
:
selectd(E,[A|As],Bs1) :-
if_(A = E, As = Bs1,
(Bs1 = [A|Bs], selectd(E,As,Bs))).
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.
如果谓词,OP并不具体
应强制执行双方都发生的项目
相同的多重性。
equal_elementsB/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.
如果我们希望第二个查询成功,我们可以通过使用元谓词以逻辑上纯粹的方式放宽定义
tfilter/3
和
具体化不平等dif/3
:
equal_elementsC([],[]).
equal_elementsC([X|Xs],Ys2) :-
selectd(X,Ys2,Ys1),
tfilter(dif(X),Ys1,Ys0),
tfilter(dif(X),Xs ,Xs0),
equal_elementsC(Xs0,Ys0).
让我们运行两个类似上面的查询,这次使用equal_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/2
并不会在以下情况下普遍终止:
?- equal_elementsB([],Xs), false. % terminates universally false. ?- equal_elementsB([_],Xs), false. % gives a single answer, but ... %%% wait forever % ... does not terminate universally
但是,如果我们翻转第一个和第二个参数,我们就会终止!
?- equal_elementsB(Xs,[]), false. % terminates universally false. ?- equal_elementsB(Xs,[_]), false. % terminates universally false.
受an answer given by @AmiTavory的启发,我们可以通过"锐化"来改进equal_elementsB/2
的实施解决方案设置如下:
equal_elementsBB(Xs,Ys) :- same_length(Xs,Ys), equal_elementsB(Xs,Ys).
为了检查非终止是否消失,我们将查询同时使用两个谓词:
?- equal_elementsB([_],Xs), false. %%% wait forever % does not terminate universally ?- equal_elementsBB([_],Xs), false. false. % terminates universally
请注意同样的"技巧"不适用于equal_elementsC/2
,
因为解决方案集的大小是无限的(对于所有感兴趣但最简单的实例)。
答案 2 :(得分:6)
紧凑形式:
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).
答案 3 :(得分:3)
其他答案都很优雅(高于我自己的Prolog级别),但令我印象深刻的是问题
对常规用途有效。
接受的答案是 O(最大值(| A | log(| A |),| B | log(| B |)),,无论列表是否相等(是否排列)或不。
至少,在打扰排序之前检查长度是值得的,这会在不同长度的情况下将运行时间减少到列表长度的线性。
扩展这一点,不难修改解决方案,以便在使用随机digests的列表不相等(直到排列)的一般情况下,其运行时是有效线性的。
假设我们定义
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)|的Prolog版本p ,其中 h 是哈希值, p 是素数。它有效地将每个列表映射到 0,....,p - 1 范围内的随机(在散列意义上)值(在上面, p 是大素数1610612741)。
我们现在可以检查两个列表是否具有相同的摘要:
same_digests(A, B) :-
digest(A, DA),
digest(B, DB),
DA =:= DB.
如果两个列表具有不同的摘要,则它们不能相等。如果两个列表具有相同的摘要,那么它们不相等的可能性很小,但仍需要检查。在这种情况下,我无耻地偷走了保罗·莫拉的优秀答案。
最终的代码是:
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).
答案 4 :(得分:1)
一种可能性,受到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).
答案 5 :(得分:0)
使用 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).
一些示例查询:
?- 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.