Prolog找到具有最大出现次数的元素

时间:2017-09-09 17:57:22

标签: prolog

我想创建一个谓词,它返回最常出现的元素,如果有多个出现次数相同的第一个:

occ([a,b,c,a,a,a,b],M).
yes M = a
occ([a,b,c,a,b],M).
yes M = a

1 个答案:

答案 0 :(得分:2)

请注意,在Prolog中,您通常会创建规则,而不是函数来解决此问题。

有很多方法可以解决这个问题,我会提供两种方法。

递归

一种方法是对列表进行递归,保持运行的计数,并且每次调用记录当前最大值,使用accumulator的示例:

% find the X with the most occurrences N in a list L
occ(X,N,L) :-
    occ(L,max(null,0),[],max(X,N)).

%% occ(+L, +CurrentMax, +Counts, +FinalMax) is det.
%
% recurse through L, using CurrentMax accumulator to
% store current candidate as a term `max(X,N)`
%
% Counts is a list in which we accumulate counts of
% occurrences to far, as list of element-count pairs X-N
%
% The final argument is unified with the CurrentMax
% accumulator as the base case

occ([], max(Xm, Nm), _, max(Xm, Nm)).

occ([X|L], max(Xm, Nm), Counts, FinalMax) :-

    % get the current count of X
    (   select(X-N, Counts, CountsT)
    ->
        N1 is N+1
    ;
        N1 = 1,
        CountsT = Counts),

    % make a new list of counts with the
    % original entry for X replaced by a new
    % one with count of N+1
    Counts2 = [X-N1 | CountsT],

    % recurse, using either new current best candidate
    % or same one depending on whether count is exceeded.
    % in case of tie, use same one, thus prioritizing first result
    (   N1 > Nm
    ->  
        occ(L, max(X,N1), Counts2, FinalMax)
    ;
        occ(L, max(Xm,Nm), Counts2, FinalMax)).

示例:

?- occ(X,N,[a,b,c,a,b]).
X = a,
N = 2.

高阶聚合操作

另一种方法是使用更高阶的聚合谓词。可以说这会导致更多的声明性代码,尽管口味会有所不同。如果您使用的是SWI-Prolog,则可以使用aggregate library。我们可以从一个规则开始计算列表中的事件(请注意我将在此处从occ/2切换到更明确的谓词):

% count number N of instances of X in a list L
element_count(X,N,L) :-
    aggregate(count,member(X,L),N).

如果您不想或不能使用aggregate/3,请查看this question previously asked on stack overflow的答案。

接下来,我们可以使用aggregate/3查找N的最大数量,以及"见证" (即具有最高值的X的值):

% count number N of instances of X in a list L, for highest N
max_element_count(X,N,L) :-
    aggregate(max(N1,X1),element_count(X1,N1,L),max(N,X)).

(如果您不使用聚合库,我会留给您制作此规则的等效实现)

让我们试一试:

?- max_element_count(X,N,[a,b,c,a,a,a,b]).
X = a,
N = 4.

在平局的情况下,似乎满足了你使用第一次出现的标准:

?- max_element_count(X,N,[a,b,c,a,b]).                                                                                                                                                         
X = a,
N = 2.

但实际上并没有保证 - 我们恰好在这里选择a,因为它在b之前按字母顺序排列。试试吧:

?- max_element_count(X,N,[b,a,c,a,b]).
X = a,
N = 2.

糟糕!

这次我们将找到列表的第一个成员,其出现次数等于max:

max_element_count2(X,N,L) :-
    aggregate(max(N1),X1,element_count(X1,N1,L),N),
    member(X,L),
    element_count(X,N,L),
    !.

这假设member/2将按顺序统一元素,这是我在Prologs中一直看到的行为,但如果标准强制要求,我不知道这一点。< / p>

演示:

?- max_element_count2(X,N,[b,a,c,a,b]).
X = b,
N = 2.