仅删除唯一元素

时间:2013-04-13 17:22:31

标签: list prolog prolog-dif

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

也就是说,调用remove_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).

很明显,为什么这不是一个理想的解决方案:countO(n)is_unique也是count,因为它只使用fail。当我们找到多个元素但最坏情况仍为O(n)时,我可以通过remove_unique来改善这一点。

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

2 个答案:

答案 0 :(得分:3)

tpartition/4同时使用 if_/3(=)/3,我们定义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).

以下是OP提供的示例查询:

?- remove_unique([1,2,2,3,4,5,7,6,7], Xs). 
Xs = [2,2,7,7].                       % succeeds deterministically

答案 1 :(得分:1)

只要您不知道列表是以任何方式排序的,并且您想要保留非唯一元素的序列,在我看来,您无法避免进行两次传递:首次计数发生,然后只选择重复元素。

如果您使用(自平衡?)二叉树来计算第二次传递期间的事件和查找,该怎么办?绝对不是O(n²),至少......