In the book Jess in Action - Rule-Based Systems in Java (written more than 10 years back; I think Drools is the system to use today?), Ernest Friedman-Hill solves the constraint problem given below using Jess, an OPS5-style forward-chaining production system written in Java. I want to solve it using Prolog.
The question is: do I solve it correctly?
The problem
A foursome of golfers is standing at a tee, in a line from left to
right. Each golfer wears different colored pants; one is wearing red
pants. The golfer to Fed’s immediate right is wearing blue pants. Joe
is second in line. Bob is wearing plaid pants. Tom isn’t in position
one or four, and he isn’t wearing the hideous orange pants.
In what
order will the four golfers tee off, and what color are each golfer’s
pants?
This is an instance of a Zebra Puzzle. See also this presentation for a beautifully illustrated solution to a more complex one.
Using Jess, by Ernest Friedman-Hill
Using the Jess production system the code would be as follows. This is from the above-mentioned book, with variables renamed for clarity.
The working memory is filled with 32 links from golfers to their possible positions and pant-colors. The find-solution rule fires for the link set fulfilling the constraints.
This seems hard to think about because one does not test "possible worlds" for whether they fulfill the constraints but one selects a set of links that fulfill the constraints. Is not clear that this indeed what one is looking for.
;; Templates for working memory, basically the links golfer<->pantscolor,
;; and golfer<->position.
(deftemplate pants-color (slot of) (slot is))
(deftemplate position (slot of) (slot is))
;; Generate all possible 'pants-color' and 'position' facts
;; 4 names, each with 4 pants-color: 16 entries
;; 4 names, each with 4 positions: 16 entries
;; This gives the 32 facts describing the links
(defrule generate-possibilities
=>
(foreach ?name (create$ Fred Joe Bob Tom)
(foreach ?color (create$ red blue plaid orange)
(assert (pants-color (of ?name) (is ?color))))
(foreach ?position (create$ 1 2 3 4)
(assert (position (of ?name) (is ?position))))))
;; The “find solution” rule forward-chains and prints out a solution
(defrule find-solution
;; There is a golfer named Fred, whose position is ?p_fred and
;; pants color is ?c_fred
(position (of Fred) (is ?p_fred))
(pants-color (of Fred) (is ?c_fred))
;; The golfer to Fred's immediate right (who is not Fred) is wearing
;; blue pants.
(position (of ?n&~Fred) (is ?p&:(eq ?p (+ ?p_fred 1))))
(pants-color (of ?n&~Fred) (is blue&~?c_fred))
;; Joe is in position #2
(position (of Joe) (is ?p_joe&2&~?p_fred))
(pants-color (of Joe) (is ?c_joe&~?c_fred))
;; Bob is wearing the plaid pants (so his position is not “n” either
;; because “n” has blue pants)
(position (of Bob) (is ?p_bob&~?p_fred&~?n&~?p_joe))
(pants-color (of Bob&~?n) (is plaid&?c_bob&~?c_fred&~?c_joe))
;; Tom isn't in position 1 or 4 and isn't wearing orange (and not blue
;; either)
(position (of Tom&~?n) (is ?p_tom&~1&~4&~?p_fred&~?p_joe&~?p_bob))
(pants-color (of Tom) (is ?c_tom&~orange&~blue&~?c_fred&~?c_joe&~?c_bob))
=>
(printout t Fred " " ?p_fred " " ?c_fred crlf)
(printout t Joe " " ?p_joe " " ?c_joe crlf)
(printout t Bob " " ?p_bob " " ?c_bob crlf)
(printout t Tom " " ?p_tom " " ?c_tom crlf crlf))
My first solution in Prolog
Turns out this is inelegant & heavy-handed (see other answers)
Let's look for a datastructure to describe the solution, given as follows: Choose a list, at each position there is a "golfer" having a "Name" and a "Pants Color": [golfer(N0,C0),golfer(N1,C1),golfer(N2,C2),golfer(N3,C3)]. Each golfer also has the teeing position from 0 to 3 given by the actual position in the list; the position is not given explicitly as in golfer(Name,Color,Position).
solution(L) :-
% select possible pants colors which must be pairwise different; for
% fast fail, we check often
is_pants_color(C0),
is_pants_color(C1),are_pairwise_different([C0,C1]),
is_pants_color(C2),are_pairwise_different([C0,C1,C2]),
is_pants_color(C3),are_pairwise_different([C0,C1,C2,C3]),
% select possible golfer names which must be pairwise different; for
% fast fail, we check often
is_name(N0),
% we know that joe is second in line, so we can plonck that condition
% in here immediately
N1 = joe,
is_name(N1),are_pairwise_different([N0,N1]),
is_name(N2),are_pairwise_different([N0,N1,N2]),
is_name(N3),are_pairwise_different([N0,N1,N2,N3]),
% instantiate the solution in a unique order (we don't change the order
% as we permute exhuastively permute colors and names)
L = [golfer(N0,C0),golfer(N1,C1),golfer(N2,C2),golfer(N3,C3)],
% tom is not in position one or four; express this clearly using
% "searchWithPosition" instead of implicitly by unification with L
search(tom,L,golfer(_,_,TomPosition)),
TomPosition \== 0,
TomPosition \== 3,
% check additional constraints using L
rightOf(fred,L,golfer(_,blue)),
search(bob,L,golfer(_,plaid,_)),
\+search(tom,L,golfer(_,hideous_orange,_)).
% here we stipulate the colors
is_pants_color(red).
is_pants_color(blue).
is_pants_color(plaid).
is_pants_color(hideous_orange).
% here we stipulate the names
is_name(joe).
is_name(bob).
is_name(tom).
is_name(fred).
% helper predicate
are_pairwise_different(L) :- sort(L,LS), length(L,Len), length(LS,Len).
% Search a golfer by name in the solution list, iteratively.
% Also return the position 0..3 for fun and profit (allows to express the
% constraint on the position)
% We "know" that names are unique, so cut on the first clause.
search(Name,L,golfer(Name,C,Pos)) :-
searchWithPosition(Name,L,golfer(Name,C,Pos),0).
searchWithPosition(Name,[golfer(Name,C)|_],golfer(Name,C,Pos),Pos) :- !.
searchWithPosition(Name,[_|R],golfer(Name,C,PosOut),PosIn) :-
PosDown is PosIn+1, searchWithPosition(Name,R,golfer(Name,C,PosOut),PosDown).
% Search the golfer to the right of another golfer by name in the list,
% iteratively. We "know" that names are unique, so cut on the first clause
rightOf(Name,[golfer(Name,_),golfer(N,C)|_],golfer(N,C)) :- !.
rightOf(Name,[_|R],golfer(N,C)) :- rightOf(Name,R,golfer(N,C)).
Let's run this:
?:- solution(L).
L = [golfer(fred, hideous_orange),
golfer(joe, blue),
golfer(tom, red),
golfer(bob, plaid)]
Compact solution
golfers(S) :-
length(G, 4),
choices([
g(1, _, _),
g(2, joe, _), % Joe is second in line.
g(3, _, _),
g(4, _, _),
g(_, _, orange),
g(_, _, red), % one is wearing red pants
g(_, bob, plaid), % Bob is wearing plaid pants
g(P, fred, _), % The golfer to Fred’s immediate right
g(Q, _, blue), % ....is wearing blue pants
g(Pos, tom, Pants) % Tom isn’t in position one or four, and
% ... he isn’t wearing the orange pants
], G),
Q is P+1,
Pos \= 1, Pos \= 4, Pants \= orange, sort(G,S).
choices([],_).
choices([C|Cs],G) :- member(C,G), choices(Cs,G).
Note added by OP: Why this works
Create a list G of 4 uninitialized elements using length/2
For every element C in the first argument passed to choices/2, make sure C is a member of G.
The first 4 entries will be assigned in order (hopefully deterministically) and as they cannot unify, this will result in something like [g(1, _G722, _G723), g(2, joe, _G730), g(3, _G736, _G737), g(4, _G743, _G744)] after the 4th call to member/2.
After choices/2 returns, G has been unified to a structure that fulfills each constraint in the list of constraints passed to choices/2, in particular:
Positions 1,2,3,4 are listed
Names joe, bob, fred, tom are listed
Colors orange, plaid, red, blue listed
...and this means we don't have to even check for whether a color or name or position appears twice - it can only appear exactly once.
Additional constraints could not be passed to choices/2 (there is no way to say things like g(P, fred, _), g(P+1, _, blue), g(not-in{1,4}, tom, not-in{orange}) and pass this to choices/2). So these additional constraints are checked via the variables unified with G contents.
If these additional constraints fail, a backtracking over choices/2 and thus over member/2 will occur. There are 9 member/2 calls on-stack at that point, which will be exhaustively tried, although backtracking back past member assignment for g(4, _, _) is not useful.
Once an acceptable solution has been found, it is sorted and the program succeeds.
Compact solution, modified
Added by OP:
The above shows that a slight improvement is possible. This program does not find any additional (identical) solutions after the first one:
golfers(G) :-
G=[g(1,_,_),g(2,_,_),g(3,_,_),g(4,_,_)],
choices([
g(2, joe, _), % Joe is second in line.
g(_, _, orange),
g(_, _, red), % one is wearing red pants
g(_, bob, plaid), % Bob is wearing plaid pants
g(P, fred, _), % The golfer to Fred’s immediate right is
g(Q, _, blue), % ...wearing blue pants
g(Pos, tom, Pants) % Tom isn’t in position one or four, and
% ...he isn’t wearing the hideous orange pants
], G),
Q is P+1,
Pos \= 1, Pos \= 4, Pants \= orange.
choices([],_).
choices([C|Cs],G) :- member(C,G), choices(Cs,G).
Why this works
Define immediately the structure of the resulting G instead of creating a list of four as-yet-unknown elements using "length"
In this "proto-G" the list elements are sorted naturally by position; we will not be finding different solutions where the g(P,_,_) are permuted by position
We can thus get rid of the g(1,_,_), g(3,_,_), g(4,_,_) constraints
If one additionally wanted to make sure that names and colors are used exactly once (which is not necessary as this must be true by construction), one would capture the names and colors via choices/2 using g(1,N1,C1), g(2,N2,C2), g(3,N3,C3), g(4,N4,C4) and make sure the Ni and Ci are unique via a sort/2: sort([N1,N2,N3,N4],[bob,fred,joe,tom]), sort([C1,C2,C3,C4],[blue,orange,plaid,red])
Another solution
Prolog make easy to write 'languages'. Let's declare the problem, and craft a micro DSL to solve:
golfers_pants([G1,G2,G3,G4]) :-
maplist(choice([G1,G2,G3,G4]),[
% my note: we are going to compute on positions, so fill the 'column' with domain values
g(1, _, _),
% Joe is second in line.
g(2, joe, _),
g(3, _, _),
g(4, _, _),
% my note: someone is wearing 'hideous orange pants' not mentioned positively elsewhere
g(_, _, orange),
% one is wearing red pants
g(_, _, red),
% Bob is wearing plaid pants
g(_, bob, plaid),
% The golfer to Fred’s immediate right is wearing blue pants
g(P, fred, _), g(Q, _, blue), Q is P+1,
% Tom isn’t in position one or four, and he isn’t wearing the hideous orange pants
g(Pos, tom, Pants), Pos \= 1, Pos \= 4, Pants \= orange
]).
choice(G,C) :- C = g(_,_,_) -> member(C,G) ; call(C).
The Jess solution, rewritten in Prolog
This is for completion.
Rewriting the Jess solution in SWI Prolog (but not in SWISH, because we now make use of assert) shows that:
There is a lot of exhaustive enumerative going on "underneath the hood"
Forward chaining production systems may not the best tool for this kind of "constraint satisfaction over a finite search space" problem
The rule conditions might profit from some conceptual cleanup
So, let's translate this directly:
% Define the possible names, colors and positions
names([fred,joe,bob,tom]).
colors([red,blue,plaid,orange]).
positions([1,2,3,4]).
run :- names(Ns),
colors(Cs),
positions(Ps),
fill_working_memory(pantscolor,Ns,Cs),
fill_working_memory(position,Ns,Ps).
fireable(SortedResult) :-
position(fred,P_fred),
pantscolor(fred,C_fred),
position(N,P) , N \== fred,
P is P_fred+1,
pantscolor(N,blue) , N \== fred,
\+member(C_fred,[blue]),
position(joe,P_joe) , P_joe == 2,
\+member(P_joe,[P_fred]),
pantscolor(joe,C_joe) , \+member(C_joe,[C_fred]),
position(bob, P_bob) , \+member(P_bob,[P_fred,N,P_joe]),
pantscolor(bob, C_bob), N \== bob,
C_bob = plaid,
\+member(C_bob, [C_fred,C_joe]),
position(tom, P_tom) , N \== tom,
\+member(P_tom,[1,4,P_fred,P_joe,P_bob]),
pantscolor(tom, C_tom), \+member(C_tom,[orange,blue,C_fred,C_joe,C_bob]),
% build clean result
Result = [g(P_fred,fred,C_fred),
g(P_bob,bob,C_bob),
g(P_joe,joe,C_joe),
g(P_tom,tom,C_tom)],
sort(Result,SortedResult).
% -- Helper to assert initial facts into the working memory
fill_working_memory(PredSym,Ns,Vs) :-
product(Ns,Vs,Cartesian),
forall(member([N,V], Cartesian), factify(PredSym,N,V)).
factify(PredSym,N,V) :- Term=..([PredSym,N,V]), writeln(Term), assertz(Term).
% -- These should be in a library somewhere --
% Via https://gist.github.com/raskasa/4282471
% pairs(+N,+Bs,-Cs)
% returns in Cs the list of pairs [N,any_element_of_B]
pairs(_,[],[]) :- !.
pairs(N,[B|Bs],[[N,B]|Cs]) :- pairs(N,Bs,Cs).
% product(+As,+Bs,-Cs)
% returns in Cs the cartesian product of lists As and Bs
% product([x,y], [a,b,c], [[x, a], [x, b], [x, c], [y, a], [y, b], [y, c]])
% Would be interesting to make this a product(+As,+Bs,?Cs)
product([],_,[]) :- !.
product([A|As],Bs,Cs) :- pairs(A,Bs,Xs),
product(As,Bs,Ys),
append(Xs,Ys,Cs).
Let's run this:
?- run, fireable(X).
X = [g(1, fred, orange),
g(2, joe, blue),
g(3, tom, red),
g(4, bob, plaid)] .
For some reason, swipl becomes dog-slow after the 5th execution or so. Garbage collection kicking in?
Related
What I have to do is to unify the possible options and solve the problem with these sentences
The Spaniard lives next to the red house.
The Norwegian lives in the blue house.
An Italian lives in the second house.
This is my attempt but I am getting an error, could someone please help me.
neighborhood(N):-
length(V,3),
next(house(_,spaniard), house(red,_), V),
member(house(blue,norway), V),
V = [_|house(_,italian)].
You may write a procedure that enforces each of your rules, and then let prolog find the possible ordering of houses that fulfill all those rules:
neiborhood(Houses):-
Houses=[House1, Italy, House3], % these are the houses, after rule 3
Italy=house(_ItalyColor, italy),
Spain=house(_SpainColor, spain),
% rule 2:
Norway=house(blue, norway),
member(House1-House3, [Spain-Norway, Norway-Spain]),
% rule 1:
append(_, [HouseA, HouseB|_], Houses),
(HouseA-HouseB=Spain-house(red, _) ; HouseB-HouseA=Spain-house(red, _)).
In this code I assumed when you said that the Spaniard lives next to the red house that it may live "to the left" or "to the right" of that house.
Also note that you only mention 2 house colors, so the third one gets unassigned color. Maybe you are missing another rule, possible which is the missing color.
Sample run:
?- neiborhood(Houses).
Houses = [house(_163550, spain), house(red, italy), house(blue, norway)] ;
Houses = [house(blue, norway), house(red, italy), house(_163550, spain)] ;
false.
In both solutions, the Spain house does not have any color assigned.
The title might be fuzzy, just see the code
number(one, odd).
number(two, even).
number(three, odd).
number(four, even).
number(five, odd).
greaterThan(five, four).
greaterThan(four, three).
greaterThan(three, two).
greaterThan(two, one).
if_larger(A,B):- elder_than(A,B).
if_larger(A,B):- elder_than(A,X),greaterThan(X,B).
even(A):- number(A, even).
odd(A):- number(A, odd).
largest(A):-
not(greaterThan(_,A)).
largestEven(A):-
even(A),
not((if_larger(X,A), even(X))).
largestOdd(A):-
odd(A),
not((if_larger(X,A), odd(X))).
how to sort the numbers in the following order: one, three, five, two, four.
I think the solution should be in the following form, however I couldn't figure them out.
next(A, Next):-
odd(A), odd(Next),
...
next(A, Next):-
even(A), even(Next),
...
next(A, Next):-
odd(A), even(Next),
...
Or, is it possible to generate a list, like [one, three, five, two, four].
I will answer my own question. The idea is using final/3 to construct a list of all the numbers, then apply insertion sort on the numbers list.
Implementation
number(five, odd).
number(one, odd).
number(two, even).
number(three, odd).
number(four, even).
greaterThan(five, four).
greaterThan(four, three).
greaterThan(three, two).
greaterThan(two, one).
even(A):- number(A, even).
odd(A):- number(A, odd).
is_larger(A,B):- greaterThan(A,B).
is_larger(A,B):- greaterThan(A,X),is_larger(X,B).
% the order considered even and odd
% which is odd before even, small before large
in_order(A,B):- odd(A), even(B).
in_order(A,B):- odd(A), odd(B), is_larger(B,A). % smaller numbers comes first
in_order(A,B):- even(A), even(B), is_larger(B,A). % smaller numbers comes first
% apply insertion sort on A
insertion_sort(A,B):- sort_helper(A, [], B).
sort_helper([], OldList, OldList).
sort_helper([H|T], OldList, Result):- insert(H, OldList, NewList), sort_helper(T, NewList, Result).
% insert(A,L,Result) put A into L
insert(A, [], [A]).
insert(A, [H|T], [A,H|T]):- in_order(A,H).
insert(A, [H|T], [H|NewList]):- not(in_order(A,H)), insert(A, T, NewList).
% Interface
oddEvenSortedList(OddEvenSortedList):- findall(A, number(A,_), Numbers), insertion_sort(Numbers, OddEvenSortedList).
Result
?- ['number.pl'].
true.
?- oddEvenSortedList(OddEvenSortedList).
OddEvenSortedList = [one, three, five, two, four].
This question is actually a modified version of my school assignment. The original question is Royal Family Succession in Prolog. I have asked a lot of friends on how to solve this problem, and finally got this solution. I will post the original question here as well.
The old Royal succession rule states that the throne is passed down along the male line according to the order of birth before the consideration along the female line – similarly according to the order of birth. Queen Elizabeth, the monarch of United Kingdom, has four offsprings; namely:- Prince Charles, Princess Ann, Prince Andrew and Prince Edward – listed in the order of birth.
First, sorry for posting the whole program, but as I don't know were the problem is I don't know which parts are irrelevant. These are two slightly different implementations of the same logic puzzle in SWI-Prolog, the first one succeeds the second one fails and I can't find the reason for the failure.
The puzzle:
4 persons are having a diner:
Donna, Doreen, David, Danny
the woman (Donna,Doreen) are sitting vis-a-vis.
the men (David,Danny) are sitting vis-a-vis.
Each of them picked a unique meal and beverage.
1) Doreen sits next to the person that ordered risotto.
2) the salad came with a coke.
3) the person with the lasagna sits vis-a-vis the person with the milk.
4) david never drinks coffee.
5) donna only drinks water.
6) danny had no appetite for risotto.
who ordered the pizza?
I choose the following approach
table with positions:
1
4 O 2
3
domain: positions{1,2,3,4}
variables: persons, meals, beverages
First the inefficient succeeding implementation:
solution(Pizza, Doreen, Donna, David, Danny) :-
% assignment of unique positions to the variables
unique(Doreen,Donna,David,Danny),
unique(Lasagna,Pizza,Risotto,Salad),
unique(Water,Coke,Coffee,Milk),
% general setting
vis_a_vis(Donna,Doreen),
vis_a_vis(David,Danny),
% the six constraints
next_to(Doreen,Risotto),
Salad = Coke,
vis_a_vis(Lasagna,Milk),
\+ David = Coffee,
Donna = Water,
\+ Danny = Risotto.
unique(X1,X2,X3,X4) :-
pos(X1),
pos(X2),
\+ X1 = X2,
pos(X3),
\+ X1 = X3, \+ X2 = X3,
pos(X4),
\+ X1 = X4, \+ X2 = X4, \+ X3 = X4.
right(1,2).
right(2,3).
right(3,4).
right(4,1).
vis_a_vis(1,3).
vis_a_vis(3,1).
vis_a_vis(2,4).
vis_a_vis(4,2).
next_to(X,Y) :- right(X,Y).
next_to(X,Y) :- right(Y,X).
pos(1).
pos(2).
pos(3).
pos(4).
This works and gives the right result. But when I try to reorder the clauses of the solution procedure to be more efficient (this is the second implementation)
solution(Pizza, Doreen, Donna, David, Danny) :-
% general setting
vis_a_vis(Donna,Doreen),
vis_a_vis(David,Danny),
% the six constraints
Salad = Coke,
vis_a_vis(Lasagna,Milk),
\+ David = Coffee,
Donna = Water,
\+ Danny = Risotto,
% assignment of unique positions to the variables
unique(Doreen,Donna,David,Danny),
unique(Lasagna,Pizza,Risotto,Salad),
unique(Water,Coke,Coffee,Milk).
%% all other predicates are like the ones in the first implementation
I get a unassigned variable warning when trying to load the file:
Warning: /home/pizza.pl:28:
Singleton variable in \+: Coffee
and the computation returns false. But shouldn't it return the same result?
I see no reason for the difference...
the warning is due to the fact that Coffe and Risotto are unbound when the negation is executed. If you replace \+ David = Coffee, by David \= Coffee, you will avoid the warning, but the solution cannot will not be computed. Should be clear indeed that since Coffee is unbound, David \= Coffee will always fail. You can use dif/2, the solution will work and will be more efficient. I've named solution1/2 your first snippet, and solution2/5 this one (using dif/2):
solution2(Pizza, Doreen, Donna, David, Danny) :-
% general setting
vis_a_vis(Donna,Doreen),
vis_a_vis(David,Danny),
% the six constraints
next_to(Doreen,Risotto), % note: you forgot this one
Salad = Coke,
vis_a_vis(Lasagna,Milk),
dif(David, Coffee),
Donna = Water,
dif(Danny, Risotto),
% assignment of unique positions to the variables
unique(Doreen,Donna,David,Danny),
unique(Lasagna,Pizza,Risotto,Salad),
unique(Water,Coke,Coffee,Milk).
a small test:
?- time(aggregate_all(count,solution1(P,A,B,C,D),N)).
% 380,475 inferences, 0.058 CPU in 0.058 seconds (100% CPU, 6564298 Lips)
N = 8.
?- time(aggregate_all(count,solution2(P,A,B,C,D),N)).
% 10,626 inferences, 0.002 CPU in 0.002 seconds (100% CPU, 4738996 Lips)
N = 8.
I'm having problem of printing the right amount of solutions for this puzzle program. It prints the correct puzzle, but not the right amount of solutions needed.
Here's what each situation does:
Puzzle 1 -
You have five colors: 2 blue, 2 green and 1 yellow
No same color may be adjacent to one another.
Puzzle 2 -
You have six colors: 1 red, 1 blue and 4 blacks
There are no more than 2 black in a row.
Puzzle 3 -
You have eight colors: 3 greens, 2 whites, 2 reds and 1 black.
The whites are never in A nor H.
There are same colors for both position D and H.
The colors in both A and G must be different colors.
The reds are never in F nor G.
The greens are never in B nor C.
On the left of every red, there's a green.
% a program that find solutions for each of the following colored ball problems with different sets of constraints.
% to run, type either
% sit1, sit2 or sit3.
% select an element for use in permutation test
%
% If the element is the head of the list, then it is in the list, and the tail is left
selectE(Element, [Element|Tail], Tail).
% If the two lists have the same head, check for more elements in the rest of the lists
selectE(Element, [Head|Tail1], [Head|Tail2]) :-
selectE(Element, Tail1, Tail2).
% generate permutations
%
% The empty list is a permutation of itself
permutationQ([],[]).
% List1 is a permutation of List2 if each element occurs in both lists
% the same number of times
permutationQ(List, [Head|Tail]) :- selectE(Head, List, Rest),
permutationQ(Rest, Tail).
%
% There are 5 colors - 2 blues, 2 greens, 1 yellow
%
sit1 :- permutationQ([green,green,blue,blue,yellow],[A,B,C,D,E]),
\+ A=B, \+ B=C, \+ C=D, \+ D=E,
printout([A,B,C,D,E]). % print any solution you find
% print solutions of sit1
printout([A,B,C,D,E]) :-
nl,
write('The order of colors from top to bottom is: '), nl,
write(A),nl,
write(B),nl,
write(C),nl,
write(D),nl,
write(E),nl.
% There are 6 colors - 1 red, 1 blue, 4 blacks,
%
sit2 :- permutationQ([black,black,black,black,red,blue],[A,B,C,D,E,F]),
((A==red -> D==blue);
(A==blue -> D==red);
(B==red -> E==blue);
(B==blue -> E==red);
(C==red -> F==blue);
(C==blue -> F==red);
(D==red -> C==blue);
(D==blue -> C==red)),
printout2([A,B,C,D,E,F]). % print any solution you find
% print solutions of sit2
printout2([A,B,C,D,E,F]) :-
nl,
write('The order of colors from top to bottom is: '), nl,
write(A),nl,
write(B),nl,
write(C),nl,
write(D),nl,
write(E),nl,
write(F),nl.
% There are 8 colors - 3 greens, 2 whites, 2 reds, 1 black
sit3 :- permutationQ([black,white,white,red,red,green,green,green],[A,B,C,D,E,F,G,H]),
% The colors in B and C are not green.
\+ B=green,
\+ C=green,
% The colors in E and F are not green because the colors in F and G are not red.
\+ E=green,
\+ F=green,
% Since red can't be in H, green can't be in G.
\+ G=green,
% The colors in D and H are the same color.
D=H,
% The colors in A and G are of different colors.
\+ A=G,
% The color in F and G are not red.
\+ F=red,
\+ G=red,
% Red can't be in A because there isn't any other position on the left for the green.
\+ A=red,
% The colors in C and D are not red because the colors in B and C are not green.
\+ C=red,
\+ D=red,
% Whites are neither A nor H.
\+ A=white,
\+ H=white,
% White is not on D because white can't be on H.
\+ D=white,
printout3([A,B,C,D,E,F,G,H]). % print any solution you find
% print solutions of sit3
printout3([A,B,C,D,E,F,G,H]) :-
nl,
write('The order of colors from top to bottom is: '), nl,
write(A),nl,
write(B),nl,
write(C),nl,
write(D),nl,
write(E),nl,
write(F),nl,
write(G),nl,
write(H),nl.
The source of your redundancies lies in the way how you use permutationQ/2. To see this, consider the goal
?- permutationQ([red,red],P).
P = [red,red]
; P = [red,red]
; false.
You are expecting one answer/solution, but you will get one solution and one redundant solution. The reason behind is that permutationQ/2 just describes all possible permutations, regardless of their actual content. To see this:
?- permutationQ([X,Y],P).
P = [X,Y]
; P = [Y,X]
; false.
The cheapest way to solve this problem is to wrap a setof(t, Goal, _) around each permutationQ/1 goal, thereby eliminating redundant solutions:
?- setof(t,permutationQ([red,red],P),_).
P = [red,red].
Generally speaking, consider to use (=)/2 and dif/2 in place of (==)/2 and (\+)/2. Also, combinatorial problems are most aptly solved with clpfd.
I am trying to create a prolog rule which will generate all the people in a social network using S number degrees of separation.
This is the rule that i have made but it is only printing empty lists. Can somebody please help me into helping me understand why this is happening and me where i am going wrong?:
socialN(_,N):- N<1,!.
socialN(_,N,_,_):- N<1,!.
socialN(P1,Separation,S1,S):-
(message(P1,P2,_); message(P2,P1,_)),
D is Separation-1,
\+(member(P2,S1)),
append(P2,S1,S2),socialN(P1,D,S2,S),!.
socialN(P2,Separation,S,S).
These are the facts:
message(allan, steve, 2013-09-03).
message(nayna, jane, 2013-09-03).
message(steve, jane, 2013-09-04).
message(steve, allan, 2013-09-04).
message(mark, martin, 2013-09-04).
message(martin, steve, 2013-09-04).
message(allan, martin, 2013-09-05).
E.g. Mark’s network includes just Martin for 1 degree of separation; it includes Martin, Steve and Allan for 2 degrees of separation; and Martin, Steve, Allan and Jane for 3.
I see you are using append and member, so I suppose you are trying to build up a list of people. I was a bit surprised that you were not using findall. Like this:
allDirectLinks(P1, L) :- findall(P2, directlyLinked(P1, P2), L).
directlyLinked(P1, P1).
directlyLinked(P1, P2) :- message(P1, P2, _).
directlyLinked(P1, P2) :- message(P2, P1, _).
From there, you can write a recursive function to find the indirect links:
socialN(0, P, [P]) :- !.
socialN(N, P1, L3) :-
N>0, !,
N1 is N-1,
socialN(N1, P1, L1)
maplist(allDirectLinks, L1, L2),
append(L2, L3).
For example, this yields in Y a list of people separated 2 steps or less from Mark:
socialN(2, mark, X), list_to_set(X, Y).
Please note, Mark himself is included in the resulting list (being a 'level 0' link); I suppose it cannot be too hard to filter that out afterwards.
I hope this makes sense; I am a bit rusty, haven't done any Prolog in 25 years.
EDIT: explanation of the rules I defined:
directlyLinked: true if there is a message between two persons (regardless of the direction of the message)
allDirectLinks: accumulates into list L all persons directly linked to a given person P1; just read the manual about findall
socialN: builds up a list of people connected to a given person (P) at a distance less than or equal to a given distance (N)
socialN(0, ...): at distance 0, every person is linked to himself
socialN(N, ...): makes a recursive call to get a list of connections at distance N-1, then uses maplist to apply allDirectLinks to every connection found, and finally uses append to concatenate the results together.