【问题标题】:How to find the cardinality of a set in Prolog?如何在 Prolog 中找到集合的基数?
【发布时间】:2016-04-28 06:26:17
【问题描述】:

我试图在 Prolog 中找到一个集合的基数。众所周知,一个集合不能有重复的元素。我试过了。

cardinal([], 0).
cardinal([_|Tail], N):-
    removeRepeated(Tail, ListWithoutRepeated),
    cardinal(ListWithoutRepeated, N1),
    N is N1 + 1.

----------------------------------------------------
consult:                                            

?- cardinal([1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5], N).
   N = 6

但正确答案是 N = 5。显然,我只计算尾部的项目,如果头部在尾部重复,则忽略。 所以我尝试了这样的事情。即头尾相加,重复上述过程。

join([], L, [L]).
join([Head|Tail], L, [Head|Tail2]):-
   join(Tail,L, Tail2).

cardinal([], 0).
cardinal([Head|Tail], N):-
    join(Tail,Head, List),
    removeRepeated(List, ListWithoutRepeated),
    cardinal(ListWithoutRepeated, N1),
    N is N1 + 1.

但是当您查询时,会生成一个无限循环。 有人可以帮我解决这个问题吗 任何人都可以帮助我如何为此编写序言语句?

编辑removeRepeated

removeRepeated([],[]).
removeRepeated([Head|Tail],ListWithoutRepeated):-
    member(Head,Tail),
    !,
    removeRepeated(Tail,ListWithoutRepeated).
removeRepeated([Head|Tail],[Head|ListWithoutRepeated]):-
    removeRepeated(Tail,ListWithoutRepeated).

----------------------------------------------------
consult:                                            

?- removeRepeated([1,1,1,1,2,2,2,3,3,3,4,4,4,8], N).
   N = [1, 2, 3, 4, 8]

【问题讨论】:

  • 你能展示一下removeRepeated的实现吗?
  • “不能”是什么意思?也许“不能拥有”?

标签: prolog


【解决方案1】:

编码:

set_cardinality(Xs, N) :-
   list_nub(Xs, Ys),
   length(Ys, N).

使用list_nub/2。可以改进此定义的终止,因为它仅在Xs 的长度固定时终止。但在我们开始讨论之前,让我们先看看你的定义。

关系名称

尝试使用真正反映您定义的关系的名称。 cardinal/2removeRepeated/2 都会给人一种印象,前者是红衣主教之间的关系,后者有所作为。但是关系没有任何作用。好吧,他们“是”。或者,它们“相关”。但这些并不是真正的动作动词。

现在开始您的第一个定义cardinal/2。实际上,我尝试阅读它。特别是,因为它包含了程序员眩晕的第一大原因——这是

递归

是的,这也让我很头晕。幸运的是,您的问题是关于 Prolog 的,在 Prolog 中,我们通常可以隐藏很多东西,但仍然可以获得很多洞察力。这是我最初看到的(我没有看到的东西被划掉)。

基数([],0)。 基数([_|尾巴],N):- removeRepeated(Tail, ListWithoutRepeated), 基数(ListWithoutRepeated, N1), N 是 N1 + 1

事实上,我可以应付。好的,空列表对应于零。对我来说听起来不错。但是,这条规则的头部是这样的:它是:

N独立于列表的第一个元素。

即:对于cardinal([1,2],N)cardinal([2,2],N),您将获得完全相同的N 值。这怎么可能是正确的?

removeRepeated/2list_nub/2 可能是一个更具关联性的名称)非常适合具有第一个参数的查询:

?- removeRepeated([1,2], X).
X = [1,2].

?- removeRepeated([1,2],[1,2]).
true.

但是,它意外地失败了:

?- removeRepeated([X,2],[1,2]).
false.    % not relational

?- X = 1, removeRepeated([X,2],[1,2]).
X = 1.

因此,虽然特化成功,但更一般的查询会失败。这清楚地表明removeRepeated/2 不是纯关系。有关按预期工作的实现,请参阅list_nub/2

【讨论】:

    【解决方案2】:

    仅使用标准谓词的解决方案是对列表进行排序,然后计算其长度:

    | ?- sort([1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5], Set), length(Set, Cardinality).
    
    Cardinality = 5
    Set = [1,2,3,4,5]
    
    yes
    

    假设sort/2standard 谓词的实现得体,该解决方案的时间复杂度应该是(最坏情况)O(N*log(N)) + O(N)。

    【讨论】:

      【解决方案3】:

      首先:有一个库 lists 随 SWI-Prolog(和一些其他版本)一起提供,其中包含可用于构造 Setlist_to_set/2:一个排序列表每个项目只出现一次。例如:

      ?- list_to_set([1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5],S),length(S,N).
      S = [1, 2, 3, 4, 5],
      N = 5.
      

      现在评论您的(第一个)解决方案。问题是您调用removeRepeated/2 时不知道cardinal/2 的列表头部当前有什么项目。一个小的修复方法是将removeRepeated/2 谓词重写为removeRepeated/3

      removeRepeated([],_,[]).
      removeRepeated([H|T],H,U) :-
          !,
          removeRepeated(T,H,U).
      removeRepeated([X|T],H,[X|U]) :-
          removeRepeated(T,H,U).
      

      然后调用它:

      cardinal([], 0).
      cardinal([H|Tail],N):-
          removeRepeated(Tail,H,ListWithoutRepeated),
          cardinal(ListWithoutRepeated, N1),
          N is N1 + 1.
      

      请注意,但它仍然效率低下,因为:

      1. 谓词的时间复杂度O(n2);和
      2. 您没有使用累加器,因此编译器可以使用尾递归

      【讨论】:

      • 非常感谢,我会花时间看看我是否可以改进这个过程。感谢您的帮助
      • 谓词 list_to_set/2 不是内置谓词,而是特定 Prolog 系统中可用的库谓词。对仅在特定 Prolog 系统中有效的通用 Prolog 问题的答案最好明确明确地说明,以避免给使用其他 Prolog 系统的读者带来麻烦。
      • @PauloMoura:我已经更新了现在提到 SWI-Prolog 的答案。尽管如此,我认为它仍然是答案的一个有价值的部分(而不是每次都重新发明轮子,看看一些图书馆)。
      • 同意。应鼓励使用可用的库,尽管用户编写自己的解决方案当然具有教学价值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多