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.