如何在Prolog中找到一组的基数?

时间:2016-04-28 06:26:18

标签: prolog

我试图在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.

但是当您查询无限循环时会生成。 有人可以帮我解决这个问题 任何人都可以帮助我,我怎么能写这个prolog声明?

修改 附上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]

3 个答案:

答案 0 :(得分:4)

代码:

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

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

关系名称

尝试使用实际反映您定义的关系的名称。 cardinal/2removeRepeated/2都会留下一个印象,即前者是红衣主教之间的关系,而后者则做了一些事情。但是关系什么都不做。好吧,他们"是"。或者,他们"关联"。但这些并不是充满动作的动词。

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

  

递归

是的,这让我也很头晕。幸运的是,你的问题是关于Prolog的,而在Prolog中,我们经常可以隐藏很多东西并且仍然可以获得很多洞察力。这是我首先看到的东西(我没有看的东西通过打击)。

cardinal([], 0).
cardinal([_|Tail], N):-
    removeRepeated(Tail, ListWithoutRepeated),
    cardinal(ListWithoutRepeated, N1),
    N is 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

答案 1 :(得分:1)

首先:有一个库lists附带SWI-Prolog(以及其他一些版本),其中包含list_to_set/2,可用于构建 Set :排序列表,其中每个项目只出现一次。例如:

?- 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(n 2 时间复杂度;和
  2. 您不使用累加器,以便编译器可以使用尾递归

答案 2 :(得分:1)

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

| ?- 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/2标准谓词有一个合适的实现,这个解决方案的时间复杂度应该是(最坏情况)O(N * log(N))+ O(N)。