Related
I recently found a small game on the Google Play app store called Cryptogram. There are dozens of apps similar to this one. The idea is to match the number to the colors such that all of the equations sound true.
I was able to get through problems 1-8 and problem 10 fairly quickly by hand, but problem 9 has proven to be more difficult for me.
Problem 9
After some time tinkering and guessing, I gave up and decided to program a solution. I have used Prolog/Datalog for some small tasks as an undergrad as well as some Project Euler problems. Previously I had seen the 15 line Sudoku solver that uses Prolog's Constraint Logic Programming over Finite Domains (clpfd) library, and I decided to give it a go myself. I'm using SWI-Prolog.
:- use_module(library(clpfd)).
problem(Colors) :-
Colors = [Pink, Cyan, Yellow, Green, Purple, Red, Brown, White, Lime],
Colors ins 0..9,
all_distinct(Colors),
% The leading digit of a number can't be 0
Pink #\= 0,
Red #\= 0,
White #\= 0,
Green #\= 0,
Lime #\= 0,
Cyan #\= 0,
% I originally tried to write a predicate generalizing numbers and a list of digits
% but got in way over my head with CLPFD.
Number1_1 #= (Pink * 1000) + (Cyan * 100) + (Pink * 10) + Yellow,
Number1_2 #= (Green * 10) + Purple,
Number1_3 #= (Cyan * 100) + (Red * 10) + Purple,
Number2_1 #= (Red * 1000) + (Brown * 100) + (White * 10) + Red,
Number2_2 #= (Lime * 10) + Yellow,
Number2_3 #= (Red * 1000) + (Lime * 100) + (Purple * 10) + Pink,
Number3_1 #= (White * 1000) + (Purple * 100) + (Cyan * 10) + White,
Number3_2 #= (Green * 1000) + (Cyan * 100) + (Yellow * 10) + Purple,
Number3_3 #= (Cyan * 1000) + (Red * 100) + (Yellow * 10) + Red,
% I'm not 100% sure whether to use floored or truncated division here.
% I thought the difference would be a float vs integer output,
% but that doesn't make sense with finite domains.
Number1_1 // Number1_2 #= Number1_3,
Number1_1 rem Number1_2 #= 0,
Number2_3 #= Number2_1 + Number2_2,
Number3_3 #= Number3_1 - Number3_2,
Number3_1 #= Number1_1 - Number2_1,
Number3_2 #= Number1_2 * Number2_2,
Number3_3 #= Number1_3 + Number2_3.
The output when I run this query in SWI-Prolog makes me feel like I'm misunderstanding a big concept in CLPFD:
?- problem([Pink, Cyan, Yellow, Green, Purple, Red, Brown, White, Lime]).
Pink in 3..9,
_7756#=Pink+10*Purple+1000*Red+100*Lime,
_7810#=1010*Pink+100*Cyan+Yellow,
all_distinct([Pink, Cyan, Yellow, Green, Purple, Red, Brown, White|...]),
Cyan in 1..7,
_7946#=1000*Cyan+10*Yellow+101*Red,
_7994#=100*Cyan+10*Yellow+1000*Green+Purple,
_8048#=10*Cyan+100*Purple+1001*White,
_8096#=100*Cyan+Purple+10*Red,
Yellow in 0..9,
_8162#=Yellow+10*Lime,
Green in 1..7,
_8216#=10*Green+Purple,
Purple in 0..9,
Red in 1..7,
_8294#=1001*Red+100*Brown+10*White,
Brown in 0..9,
White in 2..8,
Lime in 1..9,
_7756 in 1103..7568,
_8096+_7756#=_7946,
_8294+_8162#=_7756,
_8096 in 110..779,
_7810//_8216#=_8096,
_7810 in 3334..9799,
_8048+_8294#=_7810,
_7810 rem _8216#=0,
_8048 in 2313..8778,
_7946+_7994#=_8048,
_7946 in 1213..7678,
_7994 in 1100..7565,
_8216*_8162#=_7994,
_8216 in 12..79,
_8162 in 14..99,
_8294 in 1021..7486.
I would expect each color in the color list to bind to a single distinct integer in the range 0..9, but that's not what's happening. Can you help me find the solution to this problem?
EDIT
So I picked an arbitrary color and started assigning it numbers in the range that the constraint says should be valid. I ran this query with Cyan bound to 1.
?- problem([Pink, 1, Yellow, Green, Purple, Red, Brown, White, Lime]).
false.
Which doesn't make sense. The previous "output" says "Cyan in 1..7", which I thought meant that any value in that range is valid. However, if I pick another arbitrary value for Cyan:
?- problem([Pink, 2, Yellow, Green, Purple, Red, Brown, White, Lime]).
Pink = 7,
Yellow = 6,
Green = 3,
Purple = 4,
Red = 1,
Brown = 8,
White = 5,
Lime = 9.
I get the answer I was looking for. Though the Cryptogram is solved, I still don't understand why Prolog's CLPFD library didn't find it completely independently.
EDIT 2
I used your suggestions to clean up the code. I also reintroduced the predicate which relates digits to numbers. This code chunk works perfectly.
:- use_module(library(clpfd)).
digit_number(0, [], 1).
digit_number(Number, [Digit|Tail], DigitPlace) :-
digit_number(NextNumber, Tail, NextDigitPlace),
DigitPlace #= NextDigitPlace * 10,
PlaceNumber #= Digit * (NextDigitPlace),
Number #= PlaceNumber + NextNumber.
digit_number(Number, ColorList) :-
digit_number(Number, ColorList, _).
problem(Colors) :-
Colors = [Pink, Cyan, Yellow, Green, Purple, Red, Brown, White, Lime],
Colors ins 0..9,
all_distinct(Colors),
digit_number(Number1_1, [Pink, Cyan, Pink, Yellow]),
digit_number(Number1_2, [Green, Purple]),
digit_number(Number1_3, [Cyan, Red, Purple]),
digit_number(Number2_1, [Red, Brown, White, Red]),
digit_number(Number2_2, [Lime, Yellow]),
digit_number(Number2_3, [Red, Lime, Purple, Pink]),
digit_number(Number3_1, [White, Purple, Cyan, White]),
digit_number(Number3_2, [Green, Cyan, Yellow, Purple]),
digit_number(Number3_3, [Cyan, Red, Yellow, Red]),
Number1_1 // Number1_2 #= Number1_3,
Number1_1 rem Number1_2 #= 0,
Number2_1 + Number2_2 #= Number2_3,
Number3_1 - Number3_2 #= Number3_3,
Number1_1 - Number2_1 #= Number3_1,
Number1_2 * Number2_2 #= Number3_2,
Number1_3 + Number2_3 #= Number3_3,
label(Colors).
Your code works, just add label(C) :
?- problem(C), label(C).
C = [7, 2, 6, 3, 4, 1, 8, 5, 9] .
The other answer shows you one way of getting the result you want, but I would like to answer some of your questions.
I still don't understand why Prolog's CLPFD library didn't find it completely independently.
Prolog is a more-or-less declarative programming language, but (although we like to pretend, for propaganda reasons) you cannot just write down anything that is logically equivalent to your problem and expect it to be executed correctly and efficiently. In particular, the order of execution of different goals matters a lot, even though it should make no logical difference. This is especially true for arithmetic. Consider:
?- between(1, 99999999, N), N > 99999998.
N = 99999999. % correct but slooooow
?- N > 99999998, between(1, 99999999, N).
ERROR: >/2: Arguments are not sufficiently instantiated
Doing the same with CLP(FD) works much more nicely:
?- N in 1..99999999, N #> 99999998.
N = 99999999. % correct and fast!
?- N #> 99999998, N in 1..99999999.
N = 99999999. % also correct, also fast!
CLP(FD) allows you to write programs that are more correct, more declarative, and that can often be more efficient than other solutions, unless you hand-optimize them.
To achieve this, unlike normal Prolog, CLP(FD) separates the collection of constraints from the actual search for solutions. As your program goes along and creates constraints, CLP(FD) will make some simplifications, like in your example where it determines Cyan in 1..7 on its own, or in my example above where it can find the unique solution immediately. But in general, these simplifications do not solve the problem completely.
One reason for this is, simply, performance: Search can be slow. It can be faster if more constraints are known, because new constraints on already constrainted variables can only make the search space smaller, but never bigger! It makes sense to delay it until concrete answers are actually needed.
For this reason, to actually get concrete resuls, you need to call a labeling predicate that systematically enumerates solutions. In SWI-Prolog, simple ones are indomain/1 and label/1; a general one is labeling/2. This latter one even allows you to influence the search space exploration strategy, which can be useful if you have some understanding of the problem domain.
The previous "output" says "Cyan in 1..7", which I thought meant that any value in that range is valid.
Not quite: It means that if there is a valid solution for Cyan, then it is in the range 1 to 7. It doesn't give a guarantee that all values in that range are solutions. For example:
?- X in 1..5, Y in 1..5, X #< Y.
X in 1..4,
X#=<Y+ -1,
Y in 2..5.
3 is in the range 1..4, and 3 is in the range 2..5, so purely based on this we might expect a solution with X = 3 and Y = 3. But that is impossible due to the additional constraint. Only labeling will actually give you answers that are guaranteed solutions, and only if you label all the variables in the query.
See also the very nice answer here: https://stackoverflow.com/a/27218564/4391743
Edit:
% I'm not 100% sure whether to use floored or truncated division here.
% I thought the difference would be a float vs integer output,
% but that doesn't make sense with finite domains.
Number1_1 // Number1_2 #= Number1_3,
Indeed fractional division doesn't make sense here, but Prolog would have told you:
?- X in 1..5, Y in 1..5, Z #= X // Y.
X in 1..5,
X//Y#=Z,
Y in 1..5,
Z in 0..5.
?- X in 1..5, Y in 1..5, Z #= X / Y.
ERROR: Domain error: `clpfd_expression' expected, found `_G6388/_G6412'
I am trying to instantiate multiple lists in Prolog of elements with only two states, the states being red and blue. Initially, I instantiate a list of elements that are only blue by creating a list of size n:
generate_list(0, []) :- !.
generate_list(N, [H | T]) :-
N2 is N-1,
H = blue,
generate_list(N2, T).
I have also defined a predicate to change the state of colors
flip(blue, red).
flip(red, blue).
I would like to use this predicate to change the color during the instantiation of a new list, however I am stuck on how to approach this problem. The default permutation and combination predicate requires you use separate predefined elements to instantiate the new list, however I have a list of elements already generated.
It's not clear from your question what you mean by "changing" the color "during the instantiation". It would be great if you could update it with a concrete example of what you want.
However, here is something that may or may not go into the direction of what you want:
color(red).
color(blue).
colorlist([]).
colorlist([C|Cs]) :-
color(C),
colorlist(Cs).
The definition of colorlist/2 says that its argument is a list, with elements satisfying color/1. Either color is fine, so any list of any permutation of elements from red and blue is a solution. We can use this already to check color lists:
?- colorlist([red, blue, blue]).
true.
And also to try to enumerate them, although the enumeration is not fair, i.e., some things that are solutions (in this case, any list containing blue) will never be generated:
?- colorlist(Cs).
Cs = [] ;
Cs = [red] ;
Cs = [red, red] ;
Cs = [red, red, red] ;
Cs = [red, red, red, red] ;
Cs = [red, red, red, red, red] . % etc.
To solve the fairness problem, we can enumerate by length: First all the length-0 lists, then all the length-1 lists, all the length-2 ones, etc. If we expose the length as a parameter, we get something that at least has the same interface as the predicate you are trying to write:
length_colorlist(N, Cs) :-
length(Cs, N),
colorlist(Cs).
This can enumerate solutions fairly and generally:
?- length_colorlist(N, Cs).
N = 0,
Cs = [] ;
N = 1,
Cs = [red] ;
N = 1,
Cs = [blue] ;
N = 2,
Cs = [red, red] ;
N = 2,
Cs = [red, blue] ;
N = 2,
Cs = [blue, red] .
And also answer more specific queries restricted by length:
?- length_colorlist(8, Cs).
Cs = [red, red, red, red, red, red, red, red] ;
Cs = [red, red, red, red, red, red, red, blue] ;
Cs = [red, red, red, red, red, red, blue, red] ;
Cs = [red, red, red, red, red, red, blue, blue] ;
Cs = [red, red, red, red, red, blue, red, red] ;
Cs = [red, red, red, red, red, blue, red, blue] ;
Cs = [red, red, red, red, red, blue, blue, red] ;
Cs = [red, red, red, red, red, blue, blue, blue] ;
Cs = [red, red, red, red, blue, red, red, red] ;
Cs = [red, red, red, red, blue, red, red, blue] .
Note: No cuts needed, and the final definition is built up as a simple composition of more generic, individually useful parts. When stuck on a problem, especially in Prolog, always try to solve individual subparts first. In this case, "how do I define lists (of any length) containing colors?" and "how do I generate lists of specific lengths?" are two distinct subproblems that are best solved separately.
test(X, Y) :-
X ins 1..3,
Y ins 1..3,
X #\= Y.
Here is my attempt at doing it. The goal would be to type this into SWI-Prolog so that this output comes out.
?- test(X, Y).
X = 1
Y = 2 ;
X = 2,
Y = 1;
X = 3,
Y = 1 ;
... etc.
I'm actually trying to solve the 8-queens problem using prolog and have this so far.
eight_queens(Qs, L) :-
Qs = [ [X1,Y1], [X2, Y2], [X3, Y3], [X4, Y4], [X5, Y5], [X6, Y6], [X7, Y7], [X8, Y8], [X9, Y9] ],
Qs ins 1..9,
X1 #\= X2,
X1 #\= X3,
...
etc.
But I keep getting this error: "Arguments are not sufficiently instantiated" for both the test function and the eight_queens problem.
Besides the observation about in/2 and ins/2 posted by #coder, that solve your imminent problem, I would add the following points that are good to keep in mind when using CLP(FD):
1. Always make labeling the last goal
First let's observe the answers for the variant marked as 2nd way using ins in #coder's post but without the goal label/1:
test(X, Y) :-
[X,Y] ins 1..3,
X #\= Y.
?- test(X,Y).
X in 1..3, % residual goal
X#\=Y, % residual goal
Y in 1..3. % residual goal
Since there is no unique answer to the query, Prolog answers with residual goals (see section A.8.8 of the CLP(FD) manual) for more information). These residual goals are constraints that are being propagated and with every additional (non-redundant) constraint the domain is narrowed. If this does not lead to a unique solution like in the example above you can get concrete values by labeling the constrained variables (e.g. with label/1). This observation suggests to use labeling as the last goal:
?- test(X,Y), label([X,Y]).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2.
This is obviously the same result as with #coders version but the three pairs (X,Y) = (1,1) ∨ (2,2) ∨ (3,3) are not considered when labeling due to the constraint X#\=Y being posted before the goal label([X,Y]). In #coder's version it is the other way around: label([X,Y]) is delivering all three pairs as possible solutions and the last goal X#\=Y is eliminating them subsequently. To see this just leave the last goal as a comment and query the predicate:
test(X,Y):- [X,Y] ins 1..3, label([X,Y]). %, X#\=Y.
?- test(X,Y).
X = Y, Y = 1 ; % <- (1,1)
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = Y, Y = 2 ; % <- (2,2)
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
X = Y, Y = 3. % <- (3,3)
The difference is minuscule in this example, so there's nothing wrong with #coder's version. But in general this might lead to a big difference if the constraints posted after labeling exclude a lot of candidates. So it's good practice to always put labeling as the last goal.
2. Separate labeling from the actual relation
Coming from the previous observations it is opportune to divide the predicate into a core relation that is posting all the constraints and labeling. Consider the restructured predicate test/2 as a template:
test(X,Y) :-
test_(X,Y,L), % the core relation
label(L). % labeling
test_(X,Y,L) :-
L=[X,Y], % variables to be labeled in a flat list
L ins 1..3,
X#\=Y.
The predicate test_/3 is describing the actual relation by posting all the necessary constraints and has a list as an additional argument that contains all the variables to be labeled. Obtaining the latter might not be trivial, depending on the data structures your arguments come with (consider for example a list of lists as an argument that you want to turn into a flat list for labeling). So the predicate test/2 is only calling test_/3 and subsequently the labeling goal. This way you have a clean and easily readable separation.
3. Try different labeling strategies
The goal label(L) is the simplest way to do labeling. It is equivalent to labeling([],L). The first argument of labeling/2 is a list of options that gives you some control over the search process, e.g. labeling([ff],L) labels the leftmost variable with the smallest domain next, in order to detect infeasibility early. Depending on the problem you are trying to solve different labeling strategies can lead to results faster or slower. See the documentation of labeling/2 for available labeling strategies and further examples.
ins is used for lists, in is used for single variable so in your example:
test(X, Y) :-
X ins 1..3,
Y ins 1..3,
X #\= Y.
X,Y are assumed to be lists. This does not produces a syntax error, but produces error when trying to run it with X,Y not being lists.
Also when using in Low..High doesn't mean that the variable is int just X=<High and X>=Low. In order to put the constraint to be integers use label/1:
:- use_module(library(clpfd)).
%using in/
test(X,Y):- X in 1..3,Y in 1..3,label([X,Y]), X#\=Y.
%2nd way using ins
test(X,Y):- [X,Y] ins 1..3, label([X,Y]), X#\=Y.
Example:
?- test(X,Y).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
false.
So we just started learning prolog and I am having a hard time wrapping my head around prolog. We had this problem for homework and I just have no idea where to start.
Define a rule weight(R, O, Y, G, B) to find out how many marbles of each color can be contained in each bag, where R, O, Y, G, and B are the number of red, orange, yellow, green, blue marbles, respectively. The weight of the marbles is 1, 2, 3, 4, 5 grams respectively. Assuming the size of the bag is big enough to hold all the marbles with the total weight of 30 grams.
If you were going to solve this problem for just (say) yellow marbles, it would probably look like this:
weight(Marbles, Weight) :- Weight #= Marbles * 3.
Then if you wanted to see how many yellow marbles you have, you would query Prolog like so:
?- weight(Marbles, 30).
Marbles = 10.
Can you generalize from here? :)
Edit
Your problem can be solved by providing a domain.
weight(R,O,Y,G,B,Weight) :-
[R,O,Y,G,B] ins 0..sup,
Weight #= R + O*2 + Y*3 + G*4 + B*5.
When you call the predicate, you will get answers in the form of domains. This is usually not what I expect, so calling label/1 will generate solutions.
?- weight(R,O,Y,G,B,30), label([R,O,Y,G,B]).
R = O, O = Y, Y = G, G = 0,
B = 6 ;
R = O, O = Y, Y = 0,
G = 5,
B = 2 ;
etc.
I've been having a novice issue with setting up a family tree in Prolog. For some reason, I can't seem to get 'sibling' to return true.
% male(+Person)
% Specifies the Person is a male.
%
male(abraham).
male(homer).
male(bartholomew).
% female(+Person)
% Specifies the Person is a female.
%
female(mona).
female(marjorie).
female(lisa).
female(margaret).
% mother(+Parent,+Child)
% Specifies Parent is a mother of Child.
%
mother(mona,homer).
mother(marjorie,bartholomew).
mother(marjorie,lisa).
mother(marjorie,margaret).
% father(+Parent,+Child)
% Specifies Parent is a father of Child.
%
father(abraham,homer).
father(homer,bartholomew).
father(homer,lisa).
father(homer,margaret).
% sibling(+Person1,+Person2)
% Specifies Person1 is a sibling of Person2
%
sibling(X,Y) :-
father(X,Z),
mother(Y,Z).
Thanks very much in advance, this issue has been driving me nuts!
as an initial step, use an established relation (mother/2 will do, as well):
sibling(X, Y) :- father(F, X), father(F, Y), X #< Y.
yields
?- sibling(X,Y).
X = bartholomew,
Y = lisa ;
X = bartholomew,
Y = margaret ;
X = lisa,
Y = margaret ;
false.