Count items bigger than 2 in a list - prolog

I have a List with numbers. (e.g : L = [1,0,0,1,3,2,3]).
I'm trying to create a predicate which takes this list and returns an integer E, which is the times of occurrences of any number bigger than 2.
For instance in the case above let's say that the predicate is called pred/2,
then pred([1,0,0,1,3,2,3],E) would return E = 2 because the only number bigger than 2 is 3 and this number occurs 2 times.
In the case that there are 2 and more different numbers bigger than 2, for instance: L = [0,1,1,2,3,4,5,6,6] would have to return 5 because there are 5 numbers that are bigger than 2.

We can use a helper predicate with accumulator count/3. We will use this accumulator store count of already encountered values greater than 2.
% we initialize accumulator with 0
count(L, Count) :- count2(L, 0, Count).
% for empty list there is no more elements grater than 2
count2([], Acum, Acum).
% otherwise we have two consider two cases
count2([H|T], Acum, Out) :-
( H > 2 ->
Acum2 is Acum + 1,
count2(T, Acum2, Out)
;
count2(T, Acum, Out)
).

I figured out the best way to go is to write some modern prolog using high-order predicates:
pred(List, Count) :-
maplist([In,Out] >> (In>2 -> Out=1 ; Out=0), List, BinaryList),
foldl([In1,In2,Out] >> (Out is In1+In2), BinaryList, 0, Count).
which works by mapping each item to 1 if it is greater than 2, otherwise to 0, and then summing the 1s and 0s.
Some tests:
?- pred([1,0,0,1,3,2,3], Count).
Count = 2.
?- pred([0,1,1,2,3,4,5,6,6], Count).
Count = 5.
Bonus: this can be generalized to counting how many elements make a predicate hold:
count(Pred, List, Count) :-
maplist([In,Out] >> (call(Pred,In) -> Out=1 ; Out=0), List, BinaryList),
foldl([In1,In2,Out] >> (Out is In1+In2), BinaryList, 0, Count).
so that we can pass the (X>2) as an argument:
?- count([X] >> (X>2), [1,0,0,1,3,2,3], Count).
Count = 2.
or something else:
?- count([X] >> (X<2), [1,0,0,1,3,2,3], Count).
Count = 4.

Related

How to fill a list with N elements from 1 to N accepting repetition in Prolog?

I want to create a predicate fill_list/2 to fill a list with numbers from 1 to N for Example if my query is
fill_list(3,X).
which N is 3 and X is the list, I want X to be like these examples:
X = [1 , 1 , 1];
X = [1, 2 , 2];
X = [3 , 1 ,2];
There's no constrants just the numbers be values from 1 to N
fill_list(N, L) :- length(L, N), maplist(between(1,N), L).
The idea here is to make the list of the specified length, and then specify that all the members are in the desired range of 1-N. I think this is a nice demonstration of how closely the Prolog can match the problem specification.

Prolog find the element with the max number of occurrences

I would like to create a predicate that returns the element that most often appears, if there are more than one with the same number of occurrences the first:
occ([a,b,c,a,a,a,b],M).
yes M = a
occ([a,b,c,a,b],M).
yes M = a
Note that in Prolog you would generally create rules, not functions to solve this.
There are a number of ways to approach this, I'll provide two.
Recursion
One way is to recurse over the list, keeping a running count of occurrences, and with each call recording what the current max is, an example of use of an 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)).
Example:
?- occ(X,N,[a,b,c,a,b]).
X = a,
N = 2.
Higher-order aggregate operations
An alternative approach is to use higher-order aggregate predicates. Arguably this leads to more declarative code, although tastes will vary. If you are using SWI-Prolog you can use the aggregate library. We can start with a rule to count occurrences in a list (note I'm going to switch from your occ/2 to more explicit predicates here):
% count number N of instances of X in a list L
element_count(X,N,L) :-
aggregate(count,member(X,L),N).
If you don't want to or can't use aggregate/3 then have a look at the answers to this question previously asked on stack overflow.
Next we can use aggregate/3 to find the maximum number for N, plus a "witness" (i.e. the value of X with the highest value):
% 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)).
(I'll leave it to you to make an equivalent implementation of this rule if you're not using the aggregate library)
Let's try it:
?- max_element_count(X,N,[a,b,c,a,a,a,b]).
X = a,
N = 4.
With a tie it seems to satisfy your criterion of using the first occurrence in the case of tie-breakers:
?- max_element_count(X,N,[a,b,c,a,b]).
X = a,
N = 2.
But this is not in fact guaranteed - we just happen to choose a here as it is alphabetically before b. Let's try:
?- max_element_count(X,N,[b,a,c,a,b]).
X = a,
N = 2.
Oops!
This time we will find the first member of the list whose number of occurrences is equal to the 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),
!.
This assumes that member/2 will unify with elements in order, which is the behavior I have always seen with Prologs but don't know off the top of my head if it is mandated by the standard.
To demonstrate:
?- max_element_count2(X,N,[b,a,c,a,b]).
X = b,
N = 2.

Prolog - a program that finds a group of values that sum to a particular value

I want to make a program that receives 3 arguments:
list1 of coins for example: [5,2,1]
value - sum we want to get
list of coins that sum to that particular value - this list is a sub-list of list1
(it's allowed to repeat the same element , for ex: to reach 4, we can have the list [2,2])
so the program should do 2 things:
change([5,2,1],4,[2,2]) will return Yes (cause 2+2 =4)
change([5,2],6,Coins) will return Coins = [2,2,2]
this is my attempt:
change(_,0,Res).
change([X|Xs],Sum,Cs):- Sum <X, change(Xs,Sum,Cs).
change([X|Y],Sum,[X|Res]):- Sum>=X, Sum2 is Sum - X, change([X|Y],Sum2,Res).
You need to change Res to [] for the last argument of the first rule. In addition, you should add a cut operator in the same rule to avoid getting the same result multiple times.
change(_, 0, []):-!.
change([X|Y], Sum, [X|Res]):-
Sum >= X, !, % remove the cut operator to get all solutions
Sum2 is Sum - X,
change([X|Y], Sum2, Res).
change([_|Xs],Sum,Cs):-
change(Xs, Sum, Cs).
'guessing' an element from a list can be done with member/2.
Just pay care to termination
change(_,0,[]).
change(Coins,Sum,[C|Cs]) :-
Sum > 0, member(C, Coins), Rest is Sum-C, change(Coins,Rest,Cs).

How can I get this simple Prolog predicate to "return" the right way?

So I am learning Prolog. I need to write a predicate that finds the min/max value of an integer list. For example, the query minmaxArray([4,-1,5,4,1,2,3,-2],X,Y) would return X = -2 Y = 5. Here is what I have so far:
%min/max element of a 1 item list is that item.
minmaxArray([X], X, X).
%when there is only 2 items, put the smaller element in A and the
%larger element in B
minmaxArray([X,Y], A, B) :- mymin(X,Y,Min),
A is Min, mymax(X,Y,Max), B is Max.
%when there is more than two items make a recursive call to find the min/max
%of the rest of the list.
minmaxArray([X,Y|T], A, B) :- minmaxArray([Y|T], M, K),
mymin(X,M,Temp), A is Temp, mymax(X,K,Temp2), B is Temp2.
Assume mymin and mymax predicates work properly. They return the min and max of 2 numbers.
The issue here is that for example when I query minmaxArray([4,-1,5],X,Y) it returns X = -1 Y = 5 and then again X = -1 Y = 5. I know this must be because it hits the 2nd condition on the recursive call. I only want it to return X = -1 Y = 5 one time. I tried replacing condition 3 with this:
minmaxArray([X,Y,_|T], A, B) :- minmaxArray([Y,_|T], M, K),
mymin(X,M,Temp), A is Temp, mymax(X,K,Temp2), B is Temp2.
but that crashes the program. What can I do to fix this?
Note: I know that I may not be using the terminology correctly by saying returning and saying predicate when it should be rule, etc so I apologize in advance.
Seems that your code could be simpler. This predicate does all what's needed, and attempt to show how to use some standard construct (if/then/else)
minmaxArray([X], X, X).
minmaxArray([X|R], Min, Max) :-
minmaxArray(R, Tmin, Tmax),
( X < Tmin -> Min = X ; Min = Tmin ), % or mymin(X,Tmin,Min)
( X > Tmax -> Max = X ; Max = Tmax ).
You have provided 2 ways of solving the case where there are 2 items: one explicitly for 2 items, and your general case, which then employs the 1 element case.
Solution: remove the unneeded 2-element case.
Or, tail-recursive:
minmax([X|Xs],Min,Max) :- % we can only find the min/max of a non-empty list.
minmax(Xs,(X,X),Min,Max) % invoke the helper with the min/max accumulators seeded with the first item
.
minmax([],(Min,Max),Min,Max). % when the source list is exhausted, we're done: unify the accumulators with the result
minmax([X|Xs],(M,N),Min,Max) :- % when the source list is non-empty
min(X,M,M1) , % - get a new min value for the accumulator
max(X,N,N1) , % - get a new max value for the accumulator
minmax(Xs,(M1,N1),Min,Max) % - recurse down on the tail.
.
min(X,Y,X) :- X =< Y . % X is the min if it's less than or equal to Y.
min(X,Y,Y) :- X > Y . % Y is the min if it's greater than X.
max(X,Y,X) :- X >= Y . % X is the max if it's greater than or equal to Y.
max(X,Y,Y) :- X < Y . % Y is the max if it's greater than X.

Prolog inserting multiple elements into list

I want to implement a predicate (vecLine2BitLine) which does the following:
get two lists and a number the first list is the length of blocks (the elements of the blocks are '$') and the second list contains the indexes that these blocks should be placed at meaning:
vecLine2BitLine([1,2,1],[2,5,9],12,BitLine).
BitLine=[' ','$',' ',' ','$','$',' ',' ','$',' ',' ',' '].
explanation:a block of length 1 is at index 2
and a block of length 2 is at index 5 and so on..
insert_at_mul : inserts an element N times (it works perfectly,dupli and my_flatten were implemented previously so i used them)
Ive been trying to activate insert_at_mul N times when N is the length of the list X and Y
in the predicate vecLine2BitLine.
dupli(L1,N,L2) :- dupli(L1,N,L2,N).
dupli([],_,[],_).
dupli([_|Xs],N,Ys,0) :- dupli(Xs,N,Ys,N).
dupli([X|Xs],N,[X|Ys],K) :- K > 0, K1 is K - 1, dupli([X|Xs],N,Ys,K1).
my_flatten(X,[X]) :- \+ is_list(X).
my_flatten([],[]).
my_flatten([X|Xs],Zs) :- my_flatten(X,Y), my_flatten(Xs,Ys), append(Y,Ys,Zs).
insert_at_mul(L,X,K,R,N):-dupli([X],N,XX) , insert_at(L,XX,K,L1) , my_flatten(L1,R).
get_num_spaces(L,N,X):-sum(L,S), X is N-S.
generate_spaces(N,L,X):- insert_at_mul(L,'',1,X,N).
vecLine2BitLineAux([],[],_,_,_).
vecLine2BitLineAux([X|Tail1],[Y|Tail2],N,L,Lnew):- insert_at_mul(L,'*',Y,Lnew,X) ,vecLine2BitLineAux(Tail1,Tail2,N,Lnew,R). // problem here!!!
vecLine2BitLine(X,Y,N,L):- get_num_spaces(X,N,Z) , generate_spaces(Z,[],ZZ) , vecLine2BitLineAux(X,Y,N,ZZ,L).
now the problem is that in the function vecLine2BitLine i cant activate insert_at_mul N times(thats what i tried to do in this code, but failed).
how can I fix vecLine2BitLine for it to work properly as in returning the correct output by actually activating the predicate insert_at_mul N times??
THANKS!
added :
vecLine2BitLine : input parameters : (L1,L2,N,Result)
N: after activating the predicate Result will be N in length.
L1: L1 is a list of numbers each number indicates the length of a block, a block is comprised of a Sequence of '$'.
L2: L2 is a list of numbers the numbers are indices for where the blocks in L1 should be placed.
example:
vecLine2BitLine([3,2],[1,5],9,BitLine).
we can look at the input better as tuples :
vecLine2BitLine[(3,1),(2,5)],9,BitLine).
(3,1) : there is a sequence of '' 3 times at index 1
(2,5) : there is a sequence of '' 2 times at index 5
in our example 9 is the length of BitLine at the end and we have to insert into the
list BitLine 3+2 of the "special chars" '*' but we have 9-(3+2) places left in the list
so we add '' in those places and then we get:
BitLine=['$','$','$','','$','$','','','',''].
This is kind of a nice problem because you can use the arguments as loop counters. The K argument gets you to the proper index. Let's just traverse the list and find a particular index as an example. Notice the base case is that you're at the right element, and the inductive case is prior to the right element.
traverse(1, [X|_], X).
traverse(N, [_|Xs], X) :- N > 0, N0 is N-1, traverse(N0, Xs, X).
We're going to apply that pattern to insert_at/4 to get to the right location in the list. Now let's write a repeat/3 predicate that repeats X N times in a new list L. This time the base case is when we've added all the repetitions we care to, and the inductive case is that we'll add another instance.
repeat(1, X, [X]).
repeat(N, X, [X|Xs]) :- N > 0, N0 is N-1, repeat(N0, X, Xs).
You can see the similarity of structure between these two. Try to combine them into a single predicate. Since this is homework, I'll stop here. You're inches from the goal.

Resources