How to find the mode of a list in prolog?

2019-05-25 02:44发布

Can someone please tell me how to find the mode of a list in Prolog?

I have tried this:

count_elt([], _, 0) :- !. 
count_elt([H|T], H, P) :- 
  count_elt(T, H, P1),
  P is P1 + 1, !. 
count_elt([_|T], E, P) :- count_elt(T, E, P). 

listMode([], 0) :- !. 
listMode([_], 1) :- !. 
listMode([H1|[H2|T]], M) :- 
  count_elt([H1|[H2|T]], H1, C),
  listMode([H2|T], C1),
  C > C1,
  M is C, !. 
listMode([_|[H2|T]], M) :- listMode([H2|T], M).

which only returns the maximum occurrences in the list. But I need to return the element which has the maximum occurrence (The most frequent value in the list).

标签: list prolog mode
3条回答
可以哭但决不认输i
2楼-- · 2019-05-25 03:09

You're actually quite close with count_elt/3, but you need more non-determinism. In short, you need to find a way to express count_elt/3 with fewer cuts, because right now you get this:

?- count_elt([a,b,a,a,c,b,d,e,a,f], Y, X).
Y = a,
X = 4.

And what you want to get is this:

?- count_elt([a,b,a,a,c,b,d,e,a,f], Y, X).
Y = a,
X = 4 ;
Y = b,
X = 2 ;
Y = c,
X = 1 ;
...
Y = f,
X = 1 ;
false.

From there you're just trying to find the solutions with the maximum value, which you can do with setof/3 or a logical expression or using the aggregate library. So fix count_elt/3 first and go from there.

Edit: some general remarks:

  • You can write [H1|[H2|T]] as [H1,H2|T], which is a bit more clear.
  • listMode/2 should probably be returning the item in the second position rather than the count. Since you need the count to do this procedure, you're probably going to need to make a listMode/3 or listMode/5 helper to manage your state during the recursion.

Edit: solution

Since @MaDu_LK has decided to show the solution, even though this is most likely homework, I thought I'd share mine, using @false's reified equality predicate:

count_of([],         _,  0).
count_of([H|Rest], E, N1) :- 
  equality_reified(H, E, Bool),
  count_of(Rest, E, N0),
  (Bool == true -> N1 is N0 + 1 ; N1 = N0).

frequency(L, I, N) :-
  sort(L, LSorted),
  member(I, LSorted),
  count_of(L, I, N).

mode(L, X) :-
  frequency(L, X, NMax),
  \+ (frequency(L, _, NBigger),
      NMax < NBigger).

This has somewhat more pleasing performance properties:

% MaDu_LK
?- time(get_mode([a,b,c,a,b,c,a,a,b,c,a,d,b], X)).
% 2,811 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 7117429 Lips)
X = a.

% mine
?- time(mode([a,b,c,a,b,c,a,a,b,c,a,d,b], X)).
% 217 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 3144928 Lips)
X = a ;
% 195 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 3305085 Lips)
false.

The other solution also produces only one mode, even if the list is multimodal:

% MaDu_LK
?- get_mode([a,a,b,b,c], X).
X = a.

% mine
?- mode([a,a,b,b,c], X).
X = a ;
X = b ;
false.

Food for thought.

查看更多
我想做一个坏孩纸
3楼-- · 2019-05-25 03:12

Hope you already got number of suggestions. Here this is working code for you.

count_elt([],_,0):-!.
count_elt([H|T],H,C):-count_elt(T,H,C1),C is C1+1,!.
count_elt([_|T],E,C):-count_elt(T,E,C).

listMode([],0) :- !.
listMode([_],1) :- !.
listMode([H1|[H2|T]], M) :-count_elt([H1|[H2|T]],H1,C),listMode([H2|T],C1),C > C1,M is C,!.
listMode([_|[H2|T]],M) :- listMode([H2|T],M).

get_mode([H|T],M):-listMode([H|T],K),count_elt([H|T],M,K).
查看更多
【Aperson】
4楼-- · 2019-05-25 03:15

You already got good advices from Daniel, about your code. I'll show a library(aggregate) alternative way to obtain the information:

mode_of_list(L, M) :-
    setof(C-E, (member(E, L), aggregate(count, member(E, L), C)), M).

test

?- mode_of_list([a,b,a,a,c,b,d,e,a,f],L).
L = [1-c, 1-d, 1-e, 1-f, 2-b, 4-a].
查看更多
登录 后发表回答