List 仅删除唯一元素

List 仅删除唯一元素,list,prolog,prolog-dif,List,Prolog,Prolog Dif,有很多关于如何删除重复项和类似问题的资源,但我似乎找不到任何关于删除唯一元素的资源。我正在使用SWI Prolog,但我不想使用内置程序来实现这一点 也就是说,调用remove\u unique([1,2,2,3,4,5,7,6,7],X)。应该会很高兴地得到X=[2,2,7,7] 显而易见的解决办法是按照 count(_, [], 0) :- !. count(E, [E | Es], A) :- S is A + 1, count(E, Es, S). count(E, [_ | E

有很多关于如何删除重复项和类似问题的资源,但我似乎找不到任何关于删除唯一元素的资源。我正在使用SWI Prolog,但我不想使用内置程序来实现这一点

也就是说,调用
remove\u unique([1,2,2,3,4,5,7,6,7],X)。
应该会很高兴地得到
X=[2,2,7,7]

显而易见的解决办法是按照

count(_, [], 0) :- !.
count(E, [E | Es], A) :-
  S is A + 1,
  count(E, Es, S).
count(E, [_ | Es], A) :-
  count(E, Es, A).

is_unique(E, Xs) :-
  count(E, Xs, 1).

remove_unique(L, R) :- remove_unique(L, L, R).
remove_unique([], _, []) :- !.
remove_unique([X | Xs], O, R) :-
  is_unique(X, O), !,
  remove_unique(Xs, O, R).
remove_unique([X | Xs], O, [X | R]) :-
  remove_unique(Xs, O, R).
很快就会明白为什么这不是一个理想的解决方案:
count
O(n)
是唯一的,因为它只使用
count
。我可以通过
fail
ing改进这一点,当我们找到多个元素时,但最坏的情况仍然是
O(n)


然后我们来到
删除\u unique
。对于每个元素,我们检查当前元素
O
中是否唯一。如果测试失败,元素将添加到下一个分支中的结果列表中。在
O(n²)
中运行,我们得到了很多推论。虽然我不认为我们能在最坏的情况下加快速度,但我们能做得比这个天真的解决方案更好吗?我能清楚地看到的唯一改进是将
count
更改为在识别出>1个元素后立即失败的内容。

只要您不知道列表是以任何方式排序的,并且您希望保留非唯一元素的顺序,在我看来,您似乎无法避免进行两次传递:第一次计数出现,然后只拾取重复的元素

如果在第二次过程中使用(自平衡?)二叉树进行计数和查找,会怎么样?绝对不是O(n²),至少…

与 并且,我们定义
remove_unique/2
如下:

remove_unique([], []). remove_unique([E|Xs0], Ys0) :- tpartition(=(E), Xs0, Es, Xs), if_(Es = [], Ys0 = Ys, append([E|Es], Ys, Ys0)), remove_unique(Xs, Ys).
您可以先排序(O(N*log(N)),然后删除唯一的元素(O(N)),然后对每个元素使用二进制搜索或堆来确定它是否唯一O(N*log(N)),这是一个很好的建议,+1!还可以查看Ulrich Neumerkel的美丽版本
list_to_set/2
,使用排序和统一有效地检测重复元素:。
?- remove_unique([1,2,2,3,4,5,7,6,7], Xs). 
Xs = [2,2,7,7].                       % succeeds deterministically