Sum, Average of elements and Count the occurence of elements in Lists in Prolog - count

how can I write three predicates in Prolog that do the following things:
1) Define the sum (X, N) predicate, which is true when N is the sum of integers from the list X.
2) Define the avg (X, N) predicate that calculates the arithmetic average of all elements of the list X, where N is the number of elements.
3) Define the predicate called count(X, Y, N), which is true if the list Y contains N element instances
X.
Could you give me the examples of them and explain to me why they work the way they do? I know there are dozens of sum and avg predicates here on Stack Overflow, but I can't really understand why they work.

Define the sum(X, N) predicate, which is true when N is the sum of integers from the list X.
You want to calculate the sum of the elements of a list. What would be the simplest instance of the problem? When the list is empty, the sum of its element is 0. How can we break down larger lists to get to the simple case? We can remove the first element of the list, calculate the sum of the remaining list, and then add the first element to the result.
This approach can be implemented with the following code:
% Simple case: Sum of the empty list
sum([], 0).
% Recursive case: Split list into first element X and remaining list XS
sum([X|XS], N) :- sum(XS, M), N is M + X.
Usage:
?- sum([1,2,3],6).
true.
?- sum([1,2,3],X).
X = 6.
The is operator does arithmetic evaluation of its right-hand side (reference), as opposed to treating M + X as a literal term. If you would use = instead of is, M + X would be treated as a literal term. You would then get the following output:
?- sum([1,2,3],6).
false.
?- sum([1,2,3],0+3+2+1).
true.
So for Prolog 6 and 0+3+2+1 are different terms, until you force arithmetic evaluation as done by is.
2) Define the avg (X, N) predicate that calculates the arithmetic average of all elements of the list X, where N is the number of elements.
This is not possible. If X is the list and N the number of elements, then the predicate has no way of outputting the average (unless you count printing the average as a side-effect, I don't think you want). To fix this, add another parameter A that represents the average: avg(X, N, A).
We can calculate the average by taking the sum of the list and dividing it by the length of the list:
avg(X, N, A) :- sum(X, S), length(X, N), A is S / N.
Usage:
?- avg([1,2,3],3,2).
true.
?- avg([1,2,3,4],N,X).
N = 4,
X = 2.5.
3) Define the predicate called count(X, Y, N), which is true if the list Y contains N element instances X.
I understand you want N to be the number of times that the number X occurs in the list Y. We can again break this down into a simple case and then try to break down the general case into smaller steps until we get to the simple case.
For an empty list, we know that X occurs zero times in that list. For a non-empty list, we can remove the first element and check of how often X occurs in the remaining list. If the first element if equal to X, then the total number of occurrences of X is one plus the number of occurrences in the remaining list. If the first element is not equal to X, then the total number of occurrences of X is equal to the number of occurrences in the remaining list.
This approach can be implemented with the following code:
% Simple case: The list is empty.
count(_, [], 0).
% Recursive case: First element is equal to X
count(X, [X|YS], N) :- count(X, YS, M), N is M + 1, !.
% Recursive case: First element is unequal to X
count(X, [Y|YS], N) :- X \= Y, count(X, YS, N).
We use _ for variables we do not care about. We could also write X instead of _, but Prolog would then give us a warning about an unused variable.
Usage:
?- count(1, [1,1,2,3], N).
N = 2.
?- count(2, [1,1,2,3], N).
N = 1.

Related

Is there an identity to find (x mod a) + (x mod b) + (x mod c) ...?

Given an integer x an array of n elements [a1,a2,a3 ... an] (both of which will be taken from input stream) , we wish to find out (x % a1) + (x % a2) + (x % a3) ... + (x % an). x will be given after the taking array as the input.
Since this is part of a bigger problem, I want to find it out preferably in O(1) or O(logn) time (excluding O(n) time for taking in the array).
When I will take the input array, I will simultaneously calculate the sum of the complete array, and wish to bring it into a format similar to (x % (a1 + a2 + a3 ... an)). Is there any identity similar to this?
This is not my actual problem. I just wish to meet my time complexity requirements. Once the array is taken as input, after that, multiple numbers will be given ... and I have to calculate the answer for each of them in O(1) or O(logn) time.
You may assume that x is always one of the elements of the array itself, i.e., there are at max n possible values of x. The array can have up to 1e5 elements and each integer element lies in the closed interval [1, 1e5].

What is the most efficient way to find all pairs of numbers from a list of integers which add up to a separate given integer?

I had an interview yesterday and was asked to give a method to find all of the pairs of numbers from a list which add up to an integer which is given separate to the list. The list can be infinitely long, but for example:
numbers = [11,1,5,27,7,18,2,4,8]
sum = 9
pairs = [(1,8),(5,4),(7,2)]
I got as far as sorting the list and eliminating all numbers greater than the sum number and then doing two nested for loops to take each index and iterate through the other numbers to check whether they sum up to the given number, but was told that there was a more efficient way of doing it...
I've been trying to figure it out but have nothing, other than doing the nested iteration backwards but that only seems marginally more efficient.
Any ideas?
This can be done in O(n) time and O(n) auxiliary space; testing for membership of a set takes O(1) time. Since the output also takes up to O(n) space, the auxiliary space should not be a significant issue.
def pairs_sum(numbers, k):
numbers_set = set(numbers)
return [(x, y) for x in numbers if (y := k - x) in numbers_set and x < y]
Example:
>>> pairs_sum([11, 1, 5, 27, 7, 18, 2, 4, 8], 9)
[(1, 8), (2, 7), (4, 5)]
It is kind of a classic and not sure stackoverflow is the right place to ask that kind of question.
Sort the list is acsending order
Two iterators one starting from the end of the list descending i1, one starting from the beginning of the list ascending i2
Loop
while i1 > i2
if (list[i1] + list[i2] == target)
store {list[i1], list[i2]) in results pairs
i1--
i2++
else if (list[i1] + list[i2] > target)
i1--
else if (list[i1] + list[i2] < target)
i2++
This should be in O(n) with n the length of the list if you avoid the sorting algorithm which can be done with a quick sort on average in O(n log n)
Note: this algorithm doesn't take into account the case where the input list have several times the same number

how to solve ALICESIE on spoj. How it has common pattern for its answer

What is the logic behind pattern i.e.(ans=(n+1)/2) in question ALICESIE on spoj.
Algorithm_given:
1.Create a list of consecutive integers from N to 2 (N, N-1, N-2, ..., 3, 2). All of those N-1numbers are initially unmarked.
2.Initially, let P equal N, and leave this number unmarked.
3.Mark all the proper divisors of P (i.e. P remains unmarked).
4.Find the largest unmarked number from 2 to P – 1, and now let P equal this number.
5.If there were no more unmarked numbers in the list, stop. Otherwise, repeat from step 3.
Find total number of unmarked numbers.
i know its O(sqrt(n)) solution but answer is expected in O(1),it can found by seeing the common pattern i.e.(N+1)/2
But how to prove it Mathematically
link: ALICESIE

Prolog Summing up the last element in lists

I got several lists in my program as facts
road(1, 2, 3)
road(2, 3, 4)
Im trying to sum up the last elements in my lists. I know how i can get the last element with the last/2 operator in Prolog. But how can i sum the last element of multiple road lists in a recursive way?
You can do in that way:
Take all last elements and put them in a list:
findall(X, road(_, _, X), L).
Sum all element in the list:
sum([], 0).
sum([H|T], S1):- sum(T, S2), S1 is H + S2.
Summing up, the solution is as follow:
countLast(Sum):- findall(X, road(_, _, X), L), sum(L, Sum).
sum([], 0).
sum([H|T], S1):- sum(T, S2), S1 is H + S2.

Prolog query fails

This is supposed to calculate the sum of two lists. The lists can be of different size.
sum([],[],[]).
sum(A,[],A).
sum([],B,B).
sum([A|Int1],[B|Int2],[C|Int3]) :-
(
C =:= A + B
;
((C =:= A), B = [])
;
((C =:= B), A = [])
),
sum(Int1,Int2,Int3).
It seems to work correctly, except when trying to find the sum of two lists. Then it gives the following error:
ERROR: =:=/2: Arguments are not sufficiently instantiated
I don't see why. There's a recursive and a basis step, what exactly is not yet instantiated and how do I fix it?
[1] While your disjunctions in the last clause are -- to some extent -- conceptually correct, Prolog considers these disjunctions in sequence. So it first considers C =:= A + B. But either A or B can be the empty list! This is what causes the error you reported, since the empty list is not allowed to occur in a numeric operation.
[2] You need to use C is A + b (assignment) i.o. C =:= A + B (numeric equivalence).
[3] If you say [A|Int1] and then A = [], then this means that [A|Int1] is not (only) a list of integers (as you claim it is) but (also) a list of lists! You probably intend to check whether the first or the second list is empty, not whether either contains the empty list.
Staying close to your original program, I would suggest to reorder and change things in the following way:
sumOf([], [], []):- !.
sumOf([], [B|Bs], [C|Cs]):- !,
C is B,
sumOf([], Bs, Cs).
sumOf([A|As], [], [C|Cs]):- !,
C is A,
sumOf(As, [], Cs).
sumOf([A|As], [B|Bs], [C|Cs]):-
C is A + B,
sumOf(As, Bs, Cs).
For example:
?- sumOf([1,2,3], [1,-90], X).
X = [2, -88, 3]
Notice my use of the cut (symbol !) in the above. This makes sure that the same answer is not given multiple times or -- more technically -- that no choicepoints are kept (and is called determinism).
You should read a tutorial or a book. Anyway, this is how you add two things to each other:
Result is A + B
This is how you could add all elements of one list:
sum([], 0). % because the sum of nothing is zero
sum([X|Xs], Sum) :-
sum(Xs, Sum0),
Sum is X + Sum0.
And this is how you could add the sums of a list of lists:
sums([], 0).
sums([L|Ls], Sums) :-
sums(Ls, Sums0),
sum(L, S),
Sums is Sums0 + S.

Resources