【问题标题】:Remove unique elements only仅删除唯一元素
【发布时间】:2013-04-13 17:22:30
【问题描述】:

有很多关于如何删除重复和类似问题的资源,但我似乎无法找到任何关于删除独特元素的资源。我正在使用 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。当我们发现不止一个元素但最坏的情况仍然是O(n) 时,我可以通过failing 来改进这一点。

那么我们来到remove_unique。对于每个元素,我们检查当前元素 is_unique 是否在 O 中。如果测试失败,该元素将被添加到下一个分支的结果列表中。在O(n²) 中运行,我们得到了很多推论。虽然我认为我们无法在最坏的情况下加快速度,但我们能比这种幼稚的解决方案做得更好吗?我可以清楚地看到的唯一改进是将count 更改为一旦识别出>1 个元素就会失败。

【问题讨论】:

  • 您可以先排序 (O(N*log(N))),然后删除唯一元素 (O(N)),然后对每个元素使用二进制搜索或堆来确定它是否是唯一的 O(N * log(N))

标签: list prolog prolog-dif


【解决方案1】:

tpartition/4if_/3(=)/3,我们这样定义remove_unique/2

删除唯一([],[])。 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

【讨论】:

    【解决方案2】:

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

    如果您在第二遍过程中使用(自平衡?)二叉树来计算出现次数和查找会怎样?绝对不是 O(n²),至少...

    【讨论】:

    • 一个好建议,+1!还可以查看 Ulrich Neumerkel 的 list_to_set/2 的精美版本,它使用排序和统一来有效地检测重复元素:git commit
    猜你喜欢
    • 2014-03-25
    • 1970-01-01
    • 2015-01-08
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多