Solving Kakuro puzzle (5x5) in Prolog - prolog

Assuming that:
A+B+C=24
E+F+G=11
J+K+L=22
N+O+P=14
A+E=17
B+F+J+N=26
C+G+K+O=15
L+P=13
How could i find a possible solution to the problem, given the constraints above, using the predicate solve/1?
My first attempt was below, with no result. Thanks in advance!
solve(L1) :-
L1 = [A,B,C,E,F,G,J,K,L,N,O,P],
A is 24-B-C,
B is 26-F-J-N,
C is 15-G-K-O,
E is 11-F-G,
E is 17-A,
J is 22-K-L,
N is 14-O-P,
L is 13-P,
write(L1).

As #lurker already said in his comment, use CLP(FD) constraints.
In addition, I recommend:
instead of solve/1, use a declarative name like solution/1. You should describe what holds for a solution, so that the relation makes sense in all directions, also for example if the solution is already given and you want to validate it.
By convention, it makes sense to let variables that stand for lists end with an s.
Separate side-effects from pure code. In fact, remove side-effects altogether. Let the prolog-toplevel do the printing for you!
For example:
:- use_module(library(clpfd)).
solution(Ls) :-
Ls = [A,B,C,E,F,G,J,K,L,N,O,P],
A #= 24-B-C,
B #= 26-F-J-N,
C #= 15-G-K-O,
E #= 11-F-G,
E #= 17-A,
J #= 22-K-L,
N #= 14-O-P,
L #= 13-P.
This already works for queries like:
?- solution(Ls), Ls ins 0..sup, label(Ls).
Ls = [6, 3, 15, 11, 0, 0, 9, 0, 13, 14, 0, 0] ;
Ls = [6, 3, 15, 11, 0, 0, 10, 0, 12, 13, 0, 1] ;
Ls = [6, 3, 15, 11, 0, 0, 11, 0, 11, 12, 0, 2] ;
etc.
I leave completing this as an easy exercise.

Related

Replicating findall in Prolog

I'm attempting to have some code that will return a single list of positions/indices where elements are found in some base list. After much searching, copying, tweaking, etc. The following code is what I have gotten to so far.
It works in SWISH, but requires that I hit next several times before I finally get a single list with all the positions of the searched for element.
How might I have it do all the answers before sending/printing back the resulting list?
pos([E|_],E,I,P) :- P = I.
pos([E|T],E,I,[P|Pt]) :- I1 is I+1, pos(T,E,I1,Pr), Pt = Pr, P = I.
pos([H|T],E,I,P) :- H\=E, I1 is I+1, pos(T,E,I1,Pr), P = Pr.
find(X,P):- a(L),pos(L,X,1,Pr), P = Pr.
a([2,1,4,5,3,2,6,2,1,21,2,1,4,7,4,3,5,2,4,6,8,2,1,37,3,2]).
Results:
?- find(2,X)
X = 1
X = [1|6]
X = [1, 6|8]
X = [1, 6, 8|11]
X = [1, 6, 8, 11|18]
X = [1, 6, 8, 11, 18|22]
X = [1, 6, 8, 11, 18, 22|26]
There are several possible solutions.
If you want to keep your code, you should write something like this (basically, you do need to call again the predicate when you find a match)
find_in([],_,_,[]).
find_in([El|TL],El,Pos,[Pos|T]):- !,
P1 is Pos + 1,
find_in(TL,El,P1,T).
find_in([_|T],El,Pos,L):-
P1 is Pos + 1,
find_in(T,El,P1,L).
find_pos(El,LPos):-
a(L),
find_in(L,El,1,LPos).
?- find_pos(2,X).
X = [1, 6, 8, 11, 18, 22, 26]
I've used the cut, but you can avoid it using \= (as you have done in your question).
If you can use built in predicates (nth0/3 or something similar and findall/3), there is a more compact solution:
find_pos(El,LPos):-
a(L),
findall(I,nth1(I,L,El),LPos).
?- find_pos(2,X).
X = [1, 6, 8, 11, 18, 22, 26]

inverse permutation in prolog

I don't understand why my code doesn't work.
An inverse permutation is a permutation in which each number and the number of the place which it occupies are exchanged. For example [3,8,5,10,9,4,6,1,7,2] -> [8,10,1,6,3,7,9,2,5,4]
inv_perm3(X,[F],length(X)):-
length([F]) == 1,
!,
nth0(F,X,length(X)).
inv_perm3(X,[F|M],N):-
nth0(F,X,N), %nth0(?Index, List, Elem)
F is F+1,
N1 is N+1,
inv_perm3(X,M,N1).
inv_perm(A,B):-
inv_perm3(A,B,1).
I get false in every input, I test it like this: inv_perm( [2,3,1], X ).
it's way simpler... a hint
?- X=[3,8,5,10,9,4,6,1,7,2],same_length(Y,X),nth1(I,X,V),nth1(V,Y,I).
X = [3, 8, 5, 10, 9, 4, 6, 1, 7|...],
Y = [_358, _364, 1, _376, _382, _388, _394, _400, _406|...],
I = 1,
V = 3 ;
X = [3, 8, 5, 10, 9, 4, 6, 1, 7|...],
Y = [_358, _364, _370, _376, _382, _388, _394, 2, _406|...],
I = 2,
V = 8 ;
...
while I've shown only 1 element of both lists, you should use forall/2 to check all elements, or findall/3 to relate both lists. Findall would allow to generate the inverse, while forall just would check for correctness

prolog improvement of an algorithm

% SEND+MORE=MONEY
solve(VarList):-
VarList=[D,E,M,N,O,R,S,Y], % Οι μεταβλητές του προβλήματος
Digits=[0,1,2,3,4,5,6,7,8,9], % Οι τιμές των μεταβλητών (τα ψηφία)
member(D,Digits),
member(E,Digits),
member(M,Digits),
member(N,Digits), % Ανάθεση τιμών στις μεταβλητές
member(O,Digits),
member(R,Digits),
member(S,Digits),
member(Y,Digits),
M=0, S=0, % Περιορισμοί
E=D,
M=D, M=E,
N=D, N=E, N=M,
O=D, O=E, O=M, O=N,
R=D, R=E, R=M, R=N, R=O,
S=D, S=E, S=M, S=N, S=O, S=R,
Y=D, Y=E, Y=M, Y=N, Y=O, Y=R, Y=S,
S*1000+E*100+N*10+D + M*1000+O*100+R*10+E =:= M*10000+O*1000+N*100+E*10+Y.
if i decrease the number of varriables VarList. does it improves its speed?
if i S*1000+E*100+N*10+D + M*1000+O*100+R*10+E =:= M*10000+O*1000+N*100+E*10+Y
before the checks does it improve its speed?
A clpfd approach, I am putting my solution in case someone is looking into this problem.
:- use_module( library( clpfd)).
puzzle(X):-
X=([S,E,N,D]+[M,O,R,E]=[M,O,N,E,Y]),
Vars=[S,E,N,D,M,O,R,Y],Vars ins 0..9,
S*1000 + E*100 + N*10 + D +
M*1000 + O*100 + R*10 + E #=
M*1000 + O*1000 + N*100 + E*10 + Y,
S#\=0, M#\=0,
all_different(Vars),
labeling([],Vars).
?- puzzle(X).
X = ([1, 8, 0, 5]+[4, 2, 7, 8]=[4, 2, 0, 8, 3])
X = ([1, 8, 0, 5]+[6, 2, 7, 8]=[6, 2, 0, 8, 3])
X = ([1, 8, 0, 5]+[9, 2, 7, 8]=[9, 2, 0, 8, 3])
X = ([1, 8, 0, 6]+[3, 2, 7, 8]=[3, 2, 0, 8, 4])
X = ([1, 8, 0, 6]+[5, 2, 7, 8]=[5, 2, 0, 8, 4])
X = ([1, 8, 0, 6]+[9, 2, 7, 8]=[9, 2, 0, 8, 4])
X = ([2, 7, 0, 4]+[5, 3, 6, 7]=[5, 3, 0, 7, 1])....
No, if you move the line
S*1000+E*100+N*10+D + M*1000+O*100+R*10+E =:= M*10000+O*1000+N*100+E*10+Y
above what you call "Περιορισμοί" ("restrictions", according to Google Translate), it will only become slower because it will needlessly perform the arithmetic calculations which would have been avoided with the restrictions weeding out the illegal digits assignments first.
You also have erroneous equations S = 0, M = 0, E = D, ... when it should have been S =\= 0, M =\= 0, E =\= D, ..., since all the digits in these numbers are required to be unique and the first digits in the numbers can't be zeroes.
Overall your code's speed can be improved, by reducing the domain of available values with each choice of a digit value, using select/3, instead of making all the choices from the same unaffected domain Digits with member/2. This will much reduce the combinatorial choices space, and all the digits picked will be different by construction obviating the inequality checks. The tag cryptarithmetic-puzzle's info page and Q&A entries should have more discussion and / or examples of this technique (also, the tag zebra-puzzle).

How does this Prolog program resolve to H=2? I don't understand the line of execution

I have the following chunk of Prolog taken from a YouTube tutorial on Prolog:
change(H, Q, D, N, P) :-
member(H, [0, 1, 2]),
member(Q, [0, 1, 2, 3, 4]),
member(D, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
member(N, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]),
S is 50*H + 25*Q + 10*D + 5*N,
S =< 100,
P is 100-S.
It's a program to make change on a dollar. H is half dollars, Q is quarters, D for dimes, N for nickels, P for pennies.
If I type change(H, 0, 0, 0, 0). as a query, it resolves to H=2. In the video, he mentions this is a program that makes change for $1, so I understand that two half dollars are $1, but I don't understand how it gets that.
My understanding of Prolog is that when I pass change(H, 0, 0, 0, 0)., it looks to find a value for H that will satisfy the conditions, so it goes to the first line and sees that 0 would work, then for the other "member" lines sees that the 0s that were passed also are correct.
It then sets S to a value, which given the above values would be S = 0. The next line makes sure it's less than or equal to 100, which 0 is, then sets P to 100-S (which is 100).
How is it not done there with H = 0? What am I missing?
member(H,[0,1,2]) binds H to either 0, 1 or 2. Since Q, D, N and P are all 0, the only value for H that will satisfy the equations at the bottom is 2.
When H=0, S will be 0, 100-S will be 100, and since P is 0, P is 100-S will fail.
When H=1, S will be 50, 100-S will be 50, and since P is 0, P is 100-S will fail.
When H=2, S will be 100, 100-S will be 0, and since P is 0, P is 100-S will succeed.
In addition to the operational explanation, I would like to suggest CLP(FD) constraints for such problems, which are easier to understand and more declarative than lower-level arithmetic predicates. For example, in SICStus Prolog, YAP and SWI:
:- use_module(library(clpfd)).
change(H, Q, D, N, P) :-
H in 0..2,
Q in 0..4,
D in 0..10,
N in 0..20,
S #= 50*H + 25*Q + 10*D + 5*N,
S #=< 100,
P #= 100-S.
Let us now reason declaratively:
If H = 0, as you ask, and the other parameters are 0, as you specified, then what are admissible values of P?
?- change(0, 0, 0, 0, P).
P = 100.
From this, we see that if all other arguments are 0, then the only valid solution for your query is P = 100. Thus, the goal change(0, 0, 0, 0, 0) will certainly fail.

Prolog - Multiplying A list with an uninstantiated list

The input to this predicate is S, a list of integers I, and a list of integers T. Assumption is that both S and I are fully instantiated, but not T. The function multiplies the the first element in I with the first element in T then sums it with the second element in I multiplied with the [...] you get the idea. However, the struggle I am having is making this function succeed when T isn't instantiated it. I don't want to use use_module(library(clpfd)) even though it may be easier.
I already have something which multiplies correctly, as a helper. To get rid of the "uninstantiated variable" issue I figured I let a variable = S div head of I, so I'd instantiate a new variable, and I'd do this for each item in list I. Then I'd generate all the permutations such that the multiplication of items in a list summed results to S
mult_prod(A, B, S) :-
sum_prod(A, B, 0, S).
mult_prod([A | As], [B | Bs], Accum, S) :-
Accum1 is Accum + A*B,
mult_prod(As, Bs, Accum1, S).
mult_prod([], [], Accum, Accum).
multSum(S, I, T) :-
My naive solution:
multSum(0, [], []).
multSum(S, [I | Is], [T | Ts]) :-
between(0, S, T),
S1 is S - I * T,
multSum(S1, Is, Ts).
Sample output:
?- multSum(42, [6, 7, 8, 9], T).
T = [0, 0, 3, 2] ;
T = [0, 1, 1, 3] ;
T = [0, 6, 0, 0] ;
T = [1, 0, 0, 4] ;
T = [1, 4, 1, 0] ;
T = [2, 2, 2, 0] ;
T = [2, 3, 0, 1] ;
T = [3, 0, 3, 0] ;
T = [3, 1, 1, 1] ;
T = [4, 0, 0, 2] ;
T = [7, 0, 0, 0] ;
false.
Potential problems:
This was just too easy; I must have overlooked something in your question or totally misinterpreted it.
Tested in SWI Prolog only; relies on between/3. If necessary, you can implement this predicate yourself.
Limited to non-negative numbers.
Exhaustively generates all possibilities, which can be quite a lot.
Probably not the most efficient implementation possible...

Resources