How to do only 2 recursive calls in Prolog? - recursion

isTallerThan2(X,Y) :- tallerThan(X,Y).
isTallerThan2(X,Y) :- tallerThan(X,Z), isTallerThan2(Z,Y).
Where I want to find where someone is taller than 2 people.
If I have lots of relations where person X is taller than person Y like this tallerThan(X,Y)and if person b is taller than person a and person c is taller than person b... then I want to find all persons c but stop there and not find persons d,e,f... etc.

Since you have your database of tallerThan/2 facts, and you trust it (that is, tallerThan/2 is a DAG), you can write your rule as simple as
isTallerThan2(X,_Y) :- tallerThan(X,A),tallerThan(X,B),A\==B.
As per your comment, the second argument isn't used, so better to write like
isTallerThan2(X) :- tallerThan(X,A),tallerThan(X,B),A\==B.
isTallerThan2(X,_Y) :- isTallerThan2(X).

Since you are not interested in the smaller Persons, you need to store only the larger person. There are two ways to build it: one is to hardcode two relations, the other one is to recursively code it with a counter variable.
Version one: Z is taller than Y, Y is taller than someone.
tallerThan2(Z):-
tallerThan(Y,_),
tallerThan(Z,Y).
For the fact base
tallerThan(marge, homer).
tallerThan(homer, bart).
tallerThan(bart, lisa).
tallerThan(lisa, maggie).
tallerThan(abe, maggie).
tallerThan(marge, abe).
the output is
?- tallerThan2(P).
P = marge ;
P = homer ;
P = bart ;
P = marge ;
false.
The second way is to count the number of relations. You know of person Y that he or she is taller as at least NN people. If person Z is taller than Y then Z is taller than at least N = NN+1 people.
taller(Y,1) :-
tallerThan(Y,_).
taller(Z,N) :-
tallerThan(Z,Y),
taller(Y,NN),
N is NN+1.
Now the test:
?- taller(P,2).
P = marge ;
P = homer ;
P = bart ;
P = marge ;
false.
Is the same. marge appears twice in the list since she is taller than homer and taller than abe; both are taller than maggie.

Related

To determine the best country base on priority factor, prolog [duplicate]

I'm trying to write a predicate that calculates which destination a group of friends will visit.
The friends list their countries of preferences like this
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
I want to write a predicate called where that takes 2 arguments to perform the calculation.
The formula I have in mind is that the first country is worth 3 points, the second one is worth 2 points, and the last one is worth 1 point.
Here's an example of what I'm trying to achieve.
?- where([marie,jean,sasha,helena,emma],Country).
peru .
So far I have this
where([], X).
where([H|T], N) :- choice(H, [A|B]), where(T,N).
It lets me iterate through all the different friends and shows their choices but I can't iterate through the list of choices and assign points to the destinations.
How should I go about iterating through the list of choices for each friend and assigning points to calculate the best destination?
While this will solve your problem, I know it uses many predicates that you have not seen. So think of this an opportunity to excel and learn a lot.
Even if you don't understand it all, there is enough detail and intermediate results in the test that you should be able to navigate your way to a proper solution you create.
Also this is by no means efficient, it was just a quick proof of concept I did to see how this could be done.
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
destinations(Destinations) :-
findall(D1,choice(_,D1),D2),
flatten(D2,D3),
list_to_set(D3,Destinations).
init_weights(Destinations,Weights) :-
empty_assoc(Assoc),
init_weights(Destinations,Assoc,Weights).
init_weights([],Weights,Weights).
init_weights([H|T],Assoc0,Weights) :-
put_assoc(H,Assoc0,0,Assoc1),
init_weights(T,Assoc1,Weights).
update_weights([C1,C2,C3],Weights0,Weights) :-
del_assoc(C1,Weights0,Value0,Weights1),
Value1 is Value0 + 3,
put_assoc(C1,Weights1,Value1,Weights2),
del_assoc(C2,Weights2,Value2,Weights3),
Value3 is Value2 + 2,
put_assoc(C2,Weights3,Value3,Weights4),
del_assoc(C3,Weights4,Value4,Weights5),
Value5 is Value4 + 1,
put_assoc(C3,Weights5,Value5,Weights).
person_weight(Person,Weights0,Weights) :-
choice(Person,[C1,C2,C3]),
update_weights([C1,C2,C3],Weights0,Weights).
people(People) :-
findall(Person,choice(Person,_),People).
choice(Destination) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
people(People),
update_choices(People,Weights0,Weights1),
cross_ref_assoc(Weights1,Weights),
max_assoc(Weights, _, Destination),
true.
cross_ref_assoc(Assoc0,Assoc) :-
assoc_to_list(Assoc0,List0),
maplist(key_reverse,List0,List),
list_to_assoc(List,Assoc).
key_reverse(Key-Value,Value-Key).
update_choices([],Weights,Weights).
update_choices([Person|People],Weights0,Weights) :-
person_weight(Person,Weights0,Weights1),
update_choices(People,Weights1,Weights).
Tests
:- begin_tests(destination).
test(destinations) :-
destinations([peru, greece, vietnam]).
test(init_weights) :-
destinations(Destinations),
init_weights(Destinations,Weights),
assoc_to_list(Weights,[greece-0, peru-0, vietnam-0]).
test(update_weights) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
update_weights([peru,greece,vietnam],Weights0,Weights),
assoc_to_list(Weights,[greece-2,peru-3,vietnam-1]).
test(person_weight) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
person_weight(jean,Weights0,Weights),
assoc_to_list(Weights,[greece-3,peru-2,vietnam-1]).
test(people) :-
people([marie,jean,sasha,helena,emma]).
test(update_choices) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
people(People),
update_choices(People,Weights0,Weights),
assoc_to_list(Weights,[greece-10,peru-12,vietnam-8]).
test(cross_ref_assoc) :-
List0 = [1-a,2-b,3-c],
list_to_assoc(List0,Assoc0),
cross_ref_assoc(Assoc0,Assoc),
assoc_to_list(Assoc,[a-1,b-2,c-3]).
test(choice) :-
choice(peru).
:- end_tests(destination).
As suggested by GuyCoder, you need an accumulator to sum each person preferences, and foldl/N allows to does exactly this.
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
where(People,Where) :-
foldl([Person,State,Updated]>>(choice(Person,C),update(State,C,Updated)),
People,
[0=greece,0=peru,0=vietnam],
Pref),
aggregate(max(S,S=W),member(S=W,Pref),max(_,_=Where)).
% sort(Pref,Sorted),
% last(Sorted,_=Where).
update(S0,[A,B,C],S3) :-
update(S0,3,A,S1),
update(S1,2,B,S2),
update(S2,1,C,S3).
update(L,V,C,U) :-
append(X,[Y=C|Z],L),
P is Y+V,
append(X,[P=C|Z],U).
I have left commented the last two goals replaced by the single goal aggregate/3, so you can try to understand the syntax...

3 Coloring a given graph to a boolean expression in a Mini-Sat format

I'm working on a project where I have to read a file representing a graph. I made the input file looks like this:
b c
a c d
a b
b
So each line signifies a node. For eg, the first line is node a which is connected to b and c and the second is node b connected to a, c, and d. So I'm not really sure where to start here. I know that we need a bunch of if and else statements such that two nodes sharing an edge cannot have the same color and each node should have at least one color. But then how can I create the node variables itself as the text file can contain any amount of nodes. I know what I have to do for the bunch of if and else statements but I do not get how can I use that to reduce it to a miniSAT format like:
p cnf 3 3
1 -2 0
2 -3 0
-1 3 0
where the first "3" represents the number of variables and the second 3 represents the number of clauses. The other lines consist of different variables such that each different number is its own variable and if it's positive then it is true else false
You're essentially asking for how to reduce 3-coloring to CNF-SAT. Let's start with how to do that, then talk about how to generate the file you need.
The basic idea behind the reduction is the following: for each node x, we'll create three propositional variables: xr, xg, and xb that indicate what color is assigned to the node (red, green, or blue). From there, we need to add in clauses to enforce the following constraints:
every node is given at least one color,
every node is given at most one color, and
no two adjacent nodes are given the same color.
To encode point (1), we can use this clause:
(xr ∨ xg ∨ xb)
To encode point (2), we can use three clauses, each of which rules out the possibility that two colors are assigned:
(¬xr ∨ ¬xg) ∧
(¬xg ∨ ¬xb) ∧
(¬xb ∨ ¬xr)
Finally, there's rule (3). We can basically adapt the above strategy to solve this problem. For each pair of adjacent nodes x and y, add in these clauses:
(¬xr ∨ ¬yr) ∧
(¬xg ∨ ¬yg) ∧
(¬xb ∨ ¬yb)
The overall formula is then made by AND-ing all of these clauses together.
Now, the question is how to generate the output file that you want to generate. There are plenty of ways you could do this. Here's a few options:
You could calculate exactly how many clauses and variables are needed directly from the number of nodes and edges in the graph, then use some clever arithmetic tricks to output each clause by doing some math to figure out which variables correspond to which indices.
You could build some internal representation of variables and clauses (for example, have a map from nodes to the number associated with its color variables, then have a list of clauses), build that representation, then iterate over it to generate the output file.
I'd personally recommend option (2), as I imagine that's probably the easiest way of doing this.
Hope this helps!

Writing a Recurrence Equation

I am confused on where the 2nd term [T(n) = 2T(n/2) + THETA(n)] is derived from when writing the recurrence equation for Merger Sort.
From the Coursera class, it was stated that the 2nd term is due to what occurs outside of the recursive calls. So my guess is because it is due to the 2 For Loops, each would go up to n/2, so the total would be counting to n:
function mergesort(m)
var list left, right
if length(m) ≤ 1
return m
else
middle = length(m) / 2
for each x in m up to middle
add x to left
for each x in m after middle
add x to right
left = mergesort(left)
right = mergesort(right)
result = merge(left, right)
return result
Any help would be appreciated.
Thanks
Yes, that's right. There's linear work done to iterate across the elements of the input list, distributing each element into either the left or right subarray. That accounts for the Θ(n) term in the recurrence.
Hope this helps!

Merge algorithm with arrays split in c>2 ways

As an example question we are asked to create a variant of merge sort where it splits array in to c>2 arrays of roughly equal size (when c = 2 it will use regular merge)
This is the solution:
cmerge(a1, a2, .... ac)
if c = 2 return Merge(a1, a2)
else
b1 = cmerge(a1, a2,...a(c/2), floor(c/2))
b2 = cmerge(a(roof(c/2)),... ac, roof(c/2))
return merge(b1,b2)
I understand where they got this solution but when it asks for the reccurrnce relationi get a bit confused. in the question they state c sorted arrays can be merged in O(n log c)
which makes the first case clear
T(n) = c(T(n/c)) + nlogc if n>= c
T(n) = theta(n^2) for n<c <----------- can someone explain where they are getting this?
I just reallly don't get where the n^2 is coming from. In the second case is there not just one call to MERGE? And merge is theta(n) if I remember.

Create grid out of number of elements

Ok here's what I'm trying to accomplish. Say I have 100 items. I want to create a "grid"(each Item consisting of an x, y point). I want the grid to be as close to a square as possible.
Is there any kind of math to determine the grid width, and grid height i'd need by just a single number?(By grid width and height I mean the number of x items, and the number of Y items)
Now that I think about it would it be efficient to take the square root of the number, say varI=sqrt(45), remove the decimal place from varI...X=varI...then Y would be varI+1?
The square root is precisely what you need.
N
x=floor(sqrt(N))
y=raise(N/x)
This is the minimum rectangle that has more than N places and is closest to a square.
Now... if you want to find a rectangle that has exactly N places and is closest to a square...that's a different problem.
You need to find a factor of N, x, that's closest
You have to run through the factors of N and find the closest to sqrt(N). Then the rectangle is x by N/x, both integers.
There are several issues to consider here. If you want your grid to be as square as possible, for many Ns it will have empty cells in it. A simple example is N=10. You can create a 3x4 grid for it, but it will have two empty cells. A 2x5 grid, on the other hand, will have no empty cells. Some Ns (prime numbers) will always have empty cells in the grid.
But if you just want the square and don't care about empty fields then generally yes, you should take the square root. Say your number is N. Then, take R = int(sqrt(N)). Next, do an integer division N/R, take the quotient and add 1 to it. This is C. The grid is RxC. Note that when N is a square (like 100), this is a special case so don't add 1 to the quotient.
Example:
N = 40
R = int(sqrt(N)) = 6
C = int(40 / 6) + 1 = 7
grid is 6x7
I was looking to solve this problem too for a grid in html/css that had fixed dimensions and where N items would fit. I ended up creating my own script for that in javascript.
If you're interested in the method and maths I used, you can read http://machinesaredigging.com/2013/05/21/jgridder-how-to-fit-elements-in-a-sized-grid/, it's all documented there. I used recursion and it works really well, you can use the same method for your own language. Hope this helps.
I explored Eli's answer and found something I'd like to point out. For the sake of generality, one must add 1 to C only if R x C (C = int(N/R)) is not exactly N. So, the exception includes both numbers with square root and numbers which are exactly the product of two integers.
For instance:
N = 12
R = 3
C = 4 (int(N/R))
Hope it helps.

Resources