Prolog:表示两个列表至少有两个共同的元素

时间:2015-12-10 04:58:20

标签: list prolog

我试图找出两个列表是否重叠。我想写的谓词  如果列表至少有两个共同的元素,则获取两个列表并返回true。

具有预期答案的示例查询:

?- overlap([13,14,15], [17,18,13,19]).
false.

?- overlap([13,14,15], [14,17,13,18,16]).
true.

然而,到目前为止,我只有一个元素可以工作。

member(M, [M|_]).
member(M, [_|T]) :-
   member(M, T).

overlap(X, Y) :-
   member(M, X),
   member(M, Y).

?- overlap([a,b,c,d], [1,2,c,d]).

如何确保检查两个元素,而不只是一个?

2 个答案:

答案 0 :(得分:4)

另一种非常接近您的代码的方法是确保两个成员不相同:

overlap(X, Y) :-
    dif(A, B),
    member(A, X), member(A, Y),
    member(B, X), member(B, Y).

由于有评论要求采用更有效的方法,因此这是一种完全不同的方法,如this answer to a very similar question

overlap(N, X, Y) :-
    sort(Xs, SX),
    sort(Ys, SY),
    append(SX, SY, All), length(All, Len_all),
    sort(All, Sorted), length(Sorted, Len_sorted),
    Len_sorted =< Len_all - 2.

简单来说,由于sort也会删除所有重复项,因此您可以通过比较排序前后的长度来计算列表中重复项的数量。一旦以这种方式编写谓词,您还会注意到您可以稍微概括一下,因此它有两个参数:列表列表和非负整数,它是所有列表之间共享的元素数:

overlap_n(LL, N) :-
    maplist(sort, LL, SLL), % sort all lists
    append(SLL, All), length(All, Len_all),
    sort(All, Sorted), length(Sorted, Len_sorted),
    N is Len_all - Len_sorted.

您现在可以将原始问题表达为:

?- overlap_n([X, Y], N), N >= 2.

答案 1 :(得分:0)

如果你的Prolog有intersection / 3,那么较短的形式可能是:

overlap(X,Y) :- intersection(X,Y,[_,_|_]).

大型重叠列表效率低下。您的方法很容易纠正和扩展:

overlap(X,Y) :-
    select(A,X,Rx), select(A,Y,Ry),
    member(B,Rx), member(B,Ry).

我会在最后添加一个剪切以避免多种解决方案......