Related
I have this code to generate a list of the Fibonacci sequence in reverse order.
fib2(0, [0]).
fib2(1, [1,0]).
fib2(N, [R,X,Y|Zs]) :-
N > 1,
N1 is N - 1,
fib2(N1, [X,Y|Zs]),
R is X + Y.
I only need the first element though. The problem is that this code also gives out a false. after the list, so all my attempts at getting the first element have failed. Is there any way I can get that first element in the list, or any other way of calculating the Nth Fibonacci number with accumulators.
Thanks in advance.
I got this logarithmic steps O(log n) solution, and even tail recursive.
Just for fun, it can also compute the n-th Lucas number:
<pre id="in">
fib(N, X) :-
powmat(N, [[0,1],[1,1]], [[1,0],[0,1]],
[[_,X],[_,_]]).
luc(N, Z) :-
powmat(N, [[0,1],[1,1]], [[1,0],[0,1]],
[[X,Y],[_,_]]), Z is 2*X+Y.
powmat(0, _, R, R) :- !.
powmat(N, A, R, S) :- N rem 2 =\= 0, !,
mulmat(A, R, H), M is N//2, mulmat(A, A, B), powmat(M, B, H, S).
powmat(N, A, R, S) :-
M is N//2, mulmat(A, A, B), powmat(M, B, R, S).
mulmat([[A11,A12],[A21,A22]],
[[B11,B12],[B21,B22]],
[[C11,C12],[C21,C22]]) :-
C11 is A11*B11+A12*B21,
C12 is A11*B12+A12*B22,
C21 is A21*B11+A22*B21,
C22 is A21*B12+A22*B22.
?- fib(100,X).
?- luc(100,X).
</pre>
<script src="http://www.dogelog.ch/lib/exchange.js"></script>
You can compare with:
https://www.wolframalpha.com/input/?i=Fibonacci[100]
https://www.wolframalpha.com/input/?i=LucasN[100]
Edit 28.06.2021:
Here is a very quick explanation why the matrix algorithm works.
We only need to show that one step of Fibonacci is linear. Namely
that this recurrence relation leads to linear matrix:
F_{n+2} = F_{n}+F_{n+1}
To see the matrix, we have to assume that the matrix M, transforms a vector b=[Fn,Fn+1] into a vector b'=[F_{n+1}, F_{n+2}]:
b' = M*b
What could this matrix be? Just solve it:
|F_{n+1}| |0*F_{n}+1*F_{n+1}| |0 1| |F_{n} |
| | = | | = | | * | |
|F_{n+2}| |1*F_{n}+1*F_{n+1}| |1 1| |F_{n+1}|
It gives out a "false" because Prolog is unsure whether there are more solutions after the first one it provides:
?- fib2(4,L).
L = [3,2,1,1,0] ; % maybe more solutions?
false. % no
This is not a problem: You can tell Prolog that there are indeed no more solutions after the first one (or that you are not interested in seeing them):
?- once(fib2(4,L)).
or
?- fib2(4,L),!.
or you can cut in each of the first clauses, telling Prolog that if the head matches, there is no point trying another clause. This gets rid of the stray "possible solution":
fib2(0, [0]) :- !.
fib2(1, [1,0]) :- !.
fib2(N, [R,X,Y|Zs]) :-
N > 1,
N1 is N - 1,
fib2(N1, [X,Y|Zs]),
R is X + Y.
What may be a problem is that the given algorithm stores all the fib(i) and performs an addition after the recursive call, which means that Prolog cannot optimize the recursive call into a loop.
For the "accumulator-based" (bottom-up) way of computing fib(N):
% -------------------------------------------------------------
% Proceed bottom-up, without using any cache, or rather a cache
% consisting of two additional arguments.
%
% ?- fib_bottomup_direct(10,F).
% F = 55.
% ------------------------------------------------------------
fib_bottomup_direct(N,F) :-
N>0,
!,
const(fib0,FA),
const(fib1,FB),
up(1,N,FA,FB,F).
fib_bottomup_direct(0,F0) :-
const(fib0,F0).
% Carve the constants fib(0) and fib(1) out of the code.
const(fib0,0).
const(fib1,1).
% Tail recursive call moving "bottom up" towards N.
%
% X: the "current point of progress"
% N: the N we want to reach
% FA: the value of fib(X-1)
% FB: the value of fib(X)
% F: The variable that will receive the final result, fib(N)
up(X,N,FA,FB,F) :-
X<N, % not there yet, compute fib(X+1)
!,
FC is FA + FB,
Xn is X + 1,
up(Xn,N,FB,FC,F).
up(N,N,_,F,F).
Then:
?- fib_bottomup_direct(11,X).
X = 89.
Several more algorithms here; a README here.
This solution uses a tick less baggage, that is carried around.
The formulas are found at the end of the wiki fibmat section:
<pre id="in">
fib(N, X) :-
powvec(N, (1,0), (0,1), (X,_)).
luc(N, Z) :-
powvec(N, (1,0), (0,1), (X,Y)), Z is X+2*Y.
powvec(0, _, R, R) :- !.
powvec(N, A, R, S) :- N rem 2 =\= 0, !,
mulvec(A, R, H), M is N//2, mulvec(A, A, B), powvec(M, B, H, S).
powvec(N, A, R, S) :-
M is N//2, mulvec(A, A, B), powvec(M, B, R, S).
mulvec((A1,A2), (B1,B2), (C1,C2)) :-
C1 is A1*(B1+B2)+A2*B1,
C2 is A1*B1+A2*B2.
?- fib(100,X).
?- luc(100,X).
</pre>
<script src="http://www.dogelog.ch/lib/exchange.js"></script>
fib2(120,X), X=[H|_], !. answers your question, binding H to the head of that reversed list, so, the 120th Fibonacci number.
Just insert the head-taking goal X=[H|_] into the query. Of course if you're really not interested in the list, you can fuse the two goals into one
fib2(120,[H|_]), !.
Your code does ~ 2N steps, which is still O(N) like an accumulator version would, so, not a big deal, it's fine as it is. The real difference is the O(N) space your version takes, v. the O(1) of the accumulator's.
But if you look closely at your code,
fib2(0, [0]).
fib2(1, [1,0]).
fib2(N, [R,X,Y|Zs]) :-
N > 1,
N1 is N - 1,
fib2(N1, [X,Y|Zs]),
R is X + Y.
you realize that it creates the N-long list of uninstantiated variables on the way down to the deepest level of recursion, then calculates them while populating the list with the calculated values on the way back up -- but only ever referring to the last two Fibonacci numbers, i.e. the first two values in that list. So you might as well make it explicit, and end up with .... an accumulator-based version, yourself!
fib3(0, 0, 0).
fib3(1, 1, 0).
fib3(N, R, X) :-
N > 1,
N1 is N - 1,
fib3(N1, X, Y),
R is X + Y.
except that it's still not tail-recursive. The way to achieve that is usually with additional argument(s) and you can see such a code in another answer here, by David Tonhofer. But hopefully you now see the clear path between it and this last one right here.
Just for fun, an even faster version of Fibonacci (even without using tail recursion) is presented below:
% -----------------------------------------------------------------------
% FAST FIBONACCI
% -----------------------------------------------------------------------
ffib(N, F) :-
ff(N, [_, F]).
ff(1, [0, 1]) :- !.
ff(N, R) :-
M is N // 2,
ff(M, [A, B]),
F1 is A^2 + B^2,
F2 is 2*A*B + B^2,
( N mod 2 =:= 0
-> R = [F1, F2]
; F3 is F1 + F2,
R = [F2, F3] ).
% -----------------------------------------------------------------------
% MOSTOWSKI COLLAPSE VERSION
% -----------------------------------------------------------------------
fib(N, X) :-
powvec(N, (1,0), (0,1), (X,_)).
powvec(0, _, R, R) :- !.
powvec(N, A, R, S) :-
N rem 2 =\= 0, !,
mulvec(A, R, H),
M is N // 2,
mulvec(A, A, B),
powvec(M, B, H, S).
powvec(N, A, R, S) :-
M is N // 2,
mulvec(A, A, B),
powvec(M, B, R, S).
mulvec((A1,A2), (B1,B2), (C1,C2)) :-
C1 is A1*(B1 + B2) + A2*B1,
C2 is A1*B1 + A2*B2.
% -----------------------------------------------------------------------
% COMPARISON
% -----------------------------------------------------------------------
comparison :-
format('n fib ffib speed~n'),
forall( between(21, 29, E),
( N is 2^E,
cputime(fib( N, F1), T1),
cputime(ffib(N, F2), T2),
F1 = F2, % confirm that both versions compute same answer!
catch(R is T1/T2, _, R = 1),
format('2^~w~|~t~2f~6+~|~t~2f~6+~|~t~2f~6+~n', [E, T1, T2, R]))).
cputime(Goal, Time) :-
T0 is cputime,
call(Goal),
Time is cputime - T0.
The time complexity of both versions (mine and #MostowskiCollapse's) is O(lg n), ignoring multiplication cost.
Some simple empirical results (time in seconds) obtained with SWI-Prolog, version 8.2.4:
?- comparison.
n fib ffib speed
2^21 0.05 0.02 3.00
2^22 0.09 0.05 2.00
2^23 0.22 0.09 2.33
2^24 0.47 0.20 2.31
2^25 1.14 0.45 2.52
2^26 2.63 1.02 2.58
2^27 5.89 2.34 2.51
2^28 12.78 5.28 2.42
2^29 28.97 12.25 2.36
true.
This one uses the Golden Ratio formula:
<pre id="in">
fib(N, S) :-
powrad(N,(1,1),(1,0),(_,X)),
powrad(N,(1,-1),(1,0),(_,Y)),
S is (X-Y)//2^N.
luc(N, S) :-
powrad(N,(1,1),(1,0),(X,_)),
powrad(N,(1,-1),(1,0),(Y,_)),
S is (X+Y)//2^N.
powrad(0, _, R, R) :- !.
powrad(N, A, R, S) :- N rem 2 =\= 0, !,
mulrad(A, R, H), M is N//2, mulrad(A, A, B), powrad(M, B, H, S).
powrad(N, A, R, S) :-
M is N//2, mulrad(A, A, B), powrad(M, B, R, S).
mulrad((A,B),(C,D),(E,F)) :-
E is A*C+B*D*5,
F is A*D+B*C.
?- fib(100,X).
?- luc(100,X).
</pre>
<script src="http://www.dogelog.ch/lib/exchange.js"></script>
The Donald Knuth based Fibonacci-by-matrix multiplication approach, as provided by
Mostowski Collapse, but more explicit.
Algorithms can be found in the a module file plus a unit tests file on github:
The principle is based on a matrix identity provided by Donald Knuth (in Donald E. Knuth. The Art of Computer Programming. Volume 1. Fundamental
Algorithms, p.80 of the second edition)
For n >= 1 we have (for n=0, the identity matrix appears on the right-hand side, but it is unclear what fib(-1) is):
n
[ fib(n+1) fib(n) ] [ 1 1 ]
[ ] = [ ]
[ fib(n) fib(n-1) ] [ 1 0 ]
But if we work with constants fib(0) and fib(1) without assuming their value to be 0 and 1 respectively (we might be working with a special Fibonacci sequence), then we must stipulate that for n >= 1:
n-1
[ fib(n+1) fib(n) ] [ fib(2) fib(1) ] [ 1 1 ]
[ ] = [ ] * [ ]
[ fib(n) fib(n-1) ] [ fib(1) fib(0) ] [ 1 0 ]
We will separately compute the the "power matrix" on the right and explicitly multiply with the "fibonacci starter matrix", thus:
const(fib0,0).
const(fib1,1).
fib_matrixmult(N,F) :-
N>=1,
!,
Pow is N-1,
const(fib0,Fib0),
const(fib1,Fib1),
Fib2 is Fib0+Fib1,
matrixpow(
Pow,
[[1,1],[1,0]],
PowMx),
matrixmult(
[[Fib2,Fib1],[Fib1,Fib0]],
PowMx,
[[_,F],[F,_]]).
fib_matrixmult(0,Fib0) :-
const(fib0,Fib0).
matrixpow(Pow, Mx, Result) :-
matrixpow_2(Pow, Mx, [[1,0],[0,1]], Result).
matrixpow_2(Pow, Mx, Accum, Result) :-
Pow > 0,
Pow mod 2 =:= 1,
!,
matrixmult(Mx, Accum, NewAccum),
Powm is Pow-1,
matrixpow_2(Powm, Mx, NewAccum, Result).
matrixpow_2(Pow, Mx, Accum, Result) :-
Pow > 0,
Pow mod 2 =:= 0,
!,
HalfPow is Pow div 2,
matrixmult(Mx, Mx, MxSq),
matrixpow_2(HalfPow, MxSq, Accum, Result).
matrixpow_2(0, _, Accum, Accum).
matrixmult([[A11,A12],[A21,A22]],
[[B11,B12],[B21,B22]],
[[C11,C12],[C21,C22]]) :-
C11 is A11*B11+A12*B21,
C12 is A11*B12+A12*B22,
C21 is A21*B11+A22*B21,
C22 is A21*B12+A22*B22.
If your starter matrix is sure to be [[1,1],[1,0]] you can collapse the two operations matrixpow/3 followed by matrixmult/3 in the main predicate into a single call to matrixpow/3.
The above algorithm computes "too much" because two of the values in the matrix of Fibonacci numbers can be deduced from the other two. We can get rid of that redundancy. Mostowski Collapse presented a compact algorithm to do just that. Hereunder expanded for comprehensibility:
The idea is to get rid of redundant operations in matrixmult/3, by using the fact that all our matrices are symmetric and actually hold
Fibonacci numbers
[ fib(n+1) fib(n) ]
[ ]
[ fib(n) fib(n-1) ]
So, if we multiply matrices A and B to yield C, we always have something of this form (even in the starter case where B is the
identity matrix):
[ A1+A2 A1 ] [ B1+B2 B1 ] [ C1+C2 C1 ]
[ ] * [ ] = [ ]
[ A1 A2 ] [ B1 B2 ] [ C1 C2 ]
We can just retain the second columns of each matrix w/o loss of
information. The operation between these vectors is not some
standard operation like multiplication, let's mark it with ⨝:
[ A1 ] [ B1 ] [ C1 ]
[ ] ⨝ [ ] = [ ]
[ A2 ] [ B2 ] [ C2 ]
where:
C1 = B1*(A1+A2) + B2*A1 or A1*(B1+B2) + A2*B1
C2 = A1*B1 + A2*B2
fib_matrixmult_streamlined(N,F) :-
N>=1,
!,
Pow is N-1,
const(fib0,Fib0),
const(fib1,Fib1),
matrixpow_streamlined(
Pow,
v(1,0),
PowVec),
matrixmult_streamlined(
v(Fib1,Fib0),
PowVec,
v(F,_)).
fib_matrixmult_streamlined(0,Fib0) :-
const(fib0,Fib0).
matrixpow_streamlined(Pow, Vec, Result) :-
matrixpow_streamlined_2(Pow, Vec, v(0,1), Result).
matrixpow_streamlined_2(Pow, Vec, Accum, Result) :-
Pow > 0,
Pow mod 2 =:= 1,
!,
matrixmult_streamlined(Vec, Accum, NewAccum),
Powm is Pow-1,
matrixpow_streamlined_2(Powm, Vec, NewAccum, Result).
matrixpow_streamlined_2(Pow, Vec, Accum, Result) :-
Pow > 0,
Pow mod 2 =:= 0,
!,
HalfPow is Pow div 2,
matrixmult_streamlined(Vec, Vec, VecVec),
matrixpow_streamlined_2(HalfPow, VecVec, Accum, Result).
matrixpow_streamlined_2(0, _, Accum, Accum).
matrixmult_streamlined(v(A1,A2),v(B1,B2),v(C1,C2)) :-
C1 is A1*(B1+B2) + A2*B1,
C2 is A1*B1 + A2*B2.
While attempting to learn Prolog I came across a good exercise which was to write a program that displays the Nth Fibonacci number. After some work I got it working and then decided to see if I could write a program that displays a range of Fibonacci numbers according to the input.
For instance the input:
?- fib_sequence(2,5,Output).
Gives the output:
?- Output = [1,1,2,3]
I am having difficulty, however, in finding a good starting point. This is what I have so far:
fib(0, 0).
fib(1, 1).
fib(N, F) :- X is N - 1, Y is N - 2, fib(X, A), fib(Y, B), F is A + B.
fib_sequence(A,B,R) :- fib(A,Y) , fib(B,Z).
I know I must assign a value to R, but I'm not sure how to assign multiple values. Any help is greatly appreciated.
Observe that your fib_sequence cannot be done in a single predicate clause: you need at least two to keep things recursive - one to produce an empty list when A is greater than B (i.e. we've exhausted the range from A to B), and another one to prepend X from fib(A,X) to a list that you are building, increment A by 1, and call fib_sequence recursively to produce the rest of the sequence.
The first predicate clause would look like this:
fib_sequence(A,B,[]) :- A > B.
The second predicate clause is a bit harder:
fib_sequence(A,B,[H|T]) :-
A =< B /* Make sure A is less than or equal to B */
, fib(A, H) /* Produce the head value from fib(A,...) */
, AA is A + 1 /* Produce A+1 */
, fib_sequence(AA, B, T). /* Produce the rest of the list */
Prolog has some helper builtin to handle numeric sequences, then as an alternative to dasblinkenlight' answer, here is an idiomatic 'query':
fib_sequence(First, Last, Seq) :-
findall(F, (between(First,Last,N), fib(N,F)), Seq).
note that it will not work out-of-the-box with your fib/2, because there is a bug: I've added a condition that avoid the endless loop you would experience trying to backtrack on fib/2 solutions:
fib(N, F) :- N > 1, % added sanity check
X is N - 1, Y is N - 2, fib(X, A), fib(Y, B), F is A + B.
Here's yet another approach. First, I redid fib a little so that it only recursively calls itself once instead of twice. To do this, I created a predicate that returns the prior the last two Fibonacci values instead of the last one:
fib(N, F) :-
fib(N, F, _).
fib(N, F, F1) :-
N > 2,
N1 is N-1,
fib(N1, F1, F0),
F is F0 + F1.
fib(1, 1, 0).
fib(2, 1, 1).
For getting the sequence, I chose an algorithm with the Fibonacci calculation built-in so that it doesn't need to call fib O(n^2) times. It does, however, need to reverse the list when complete:
fib_sequence(A, B, FS) :-
fib_seq_(A, B, FSR),
reverse(FSR, FS).
fib_sequence_(A, B, []) :-
A > B.
fib_sequence_(A, B, [F]) :-
A =:= B,
fib(A, F, _).
fib_sequence_(A, B, [F1,F0]) :-
1 is B - A,
fib(B, F1, F0).
fib_sequence_(A, B, [F2,F1,F0|FT] ) :-
B > A,
B1 is B - 1,
fib_sequence_(A, B1, [F1,F0|FT]),
F2 is F1 + F0.
Here's one more way, to do it without the reverse, but the reverse method above still appears to be a little faster in execution.
fib_sequence_dl(A, B, F) :-
fib_sequence_dl_(A, B, F, [_,_|[]]).
fib_sequence_dl_(A, B, [], _) :-
A > B, !.
fib_sequence_dl_(A, B, [F], _) :-
A =:= B,
fib(A, F, _), !.
fib_sequence_dl_(A, B, [F0,F1|T], [F0,F1|T]) :-
1 is B - A,
fib(B, F1, F0), !.
fib_sequence_dl_(A, B, F, [F1,F2|T]) :-
A < B,
B1 is B - 1,
fib_sequence_dl_(A, B1, F, [F0,F1|[F2|T]]),
F2 is F0 + F1.
I am trying to solve a puzzle in Prolog that involves taking a square of numbers (a list of a list of numbers) and returning the list of the greatest combination of numbers starting at the top and going down, row by row. Each move must be either down, down to the right, or down to the left.
I've been trying to do this for a while now, does anyone have a place I could begin?
For example, on the board
[[0, 2, 1, 0],
[0, 1, 1, 0],
[0,10,20,30]]
the best move would be [1, 2, 3] for 33 points.
So here is how you could do it. I know it's kinda wordy, that probably is because I'm not really fluent in Prolog either...
% Lookup a value in a list by it's index.
% this should be built into prolog?
at(0, [H|_], H).
at(N, [_|T], X) :-
N > 0,
N1 is N - 1,
at(N1, T, X).
% like Haskell's maximumBy; takes a predicate, a
% list and an initial maximum value, finds the
% maximum value in a list
maxby(_, [], M, M).
maxby(P, [H|T], M0, M) :-
call(P, H, M0, M1),
maxby(P, T, M1, M).
% which of two paths has the bigger score?
maxval(path(C, I), path(C1, _), path(C, I)) :- C >= C1.
maxval(path(C0, _), path(C, I), path(C, I)) :- C0 < C.
% generate N empty paths as a starting value for
% our search
initpaths(N, Ps) :-
findall(path(0, []),
between(0, N, _),
Ps).
% given the known best paths to all indexes in the previous
% line and and index I in the current line, select the best
% path leading to I.
select(Ps, I, N, P) :-
I0 is I-1,
I1 is I+1,
select(Ps, I0, N, path(-1, []), P0),
select(Ps, I, N, P0, P1),
select(Ps, I1, N, P1, P).
% given the known best paths to the previous line (Ps),
% an index I and a preliminary choice P0, select the path
% leading to the index I (in the previous line) if I is within
% the range 0..N and its score is greater than the preliminary
% choice. Stay with the latter otherwise.
select(_, I, _, P0, P0) :- I < 0.
select(_, I, N, P0, P0) :- I > N.
select(Ps, I, _, P0, P) :-
at(I, Ps, P1),
maxby(maxval, [P0], P1, P).
% given the known best paths to the previous line (P1),
% and a Row, which is the current line, extend P1 to a
% new list of paths P indicating the best paths to the
% current line.
update(P1, P, Row, N) :-
findall(path(C, [X|Is]),
( between(0, N, X)
, select(P1, X, N, path(C0, Is))
, at(X, Row, C1)
, C is C0 + C1),
P).
% solve the puzzle by starting with a list of empty paths
% and updating it as long as there are still more rows in
% the square.
solve(Rows, Score, Path) :-
Rows = [R|_],
length(R, N0),
N is N0 - 1,
initpaths(N, IP),
solve(N, Rows, IP, Score, Path).
solve(_, [], P, Score, Path) :-
maxby(maxval, P, path(-1, []), path(Score, Is0)),
reverse(Is0, Path).
solve(N, [R|Rows], P0, Score, Path) :-
update(P0, P1, R, N),
solve(N, Rows, P1, Score, Path).
Shall we try it out? Here are your examples:
?- solve([[0,2,1,0], [0,1,1,0], [0,10,20,30]], Score, Path).
Score = 33,
Path = [1, 2, 3] ;
false.
?- solve([[0,1,1], [0,2,1], [10,0,0]], Score, Path).
Score = 13,
Path = [1, 1, 0] ;
false.
My prolog is a bit shaky. In fact all I remember about prolog is that it's declarative.
Here is some haskell code to find the value of the max path. Finding the trace should be an easy next step, but a bit more complicated to code up I imagine. I suppose a very elegant solution for the trace would be using monads.
maxValue :: [ [ Int ] ] -> Int
maxValue p = maximum $ maxValueHelper p
maxValueHelper :: [ [ Int ] ] -> [ Int ]
maxValueHelper [ row ] = row
maxValueHelper ( row : restOfRows ) = combine row ( maxValueHelper restOfRows )
combine :: [ Int ] -> [ Int ]-> [ Int ]
combine [ x ] [ y ] = [ x + y ]
combine ( x1 : x2 : lx ) ( y1 : y2 : ly ) =
let ( z2 : lz ) = combine ( x2 : lx ) ( y2 : ly )
in
( max ( x1 + y1 ) ( x1 + y2 ) : max ( x2 + y1 ) z2 : lz )
main :: IO()
main = print $ maxValue [[0,2,1,0], [0,1,1,0], [0,10,20,30]]
?- best_path_score([[0, 2, 1, 0],[0, 1, 1, 0],[0,10,20,30]], P, S).
P = [1, 2, 3],
S = 33.
with this definition
best_path_score(Rs, BestPath, BestScore) :-
aggregate_all(max(Score, Path), a_path(Rs, Path, Score), max(BestScore, BestPath)).
a_path([R|Rs], [P|Ps], Score) :-
nth0(P, R, S0),
a_path(Rs, P, Ps, S),
Score is S0 + S.
a_path([], _, [], 0).
a_path([R|Rs], P, [Q|Qs], T) :-
( Q is P - 1 ; Q is P ; Q is P + 1 ),
nth0(Q, R, S0),
a_path(Rs, Q, Qs, S),
T is S0 + S.
I'm trying to generate all possible 'ways' from one element in matrix to another, Main conditional says that Element in matrix can be connected with other only if elements share at least one corner so in that matrix
[[1,2,3]
[5,4,6]
[8,9,7]]
1 can be only conected with 2,4,5 but 4 is conected with all elements. Is it possible to represent that list as graph without using attract? Or maybe I can find out any easier way to do it
Thanks for all answers.
Ok i set forth :-)
Now with predicates I generated all 'edges' but i cannot use them anywhere, I could not figure out, how to add to accumulator(list) infomrations of each cell with such pattern ([row:R1,column:C1,value:V1], [row:R2,column:C2,value:V2]).
Here's a (long, but simple) solution to consider.
Firstly, it helps to have an auxiliary predicate to retrieve an element from the Row,Col position in a matrix as a list of lists, such as:
get_matrix_entry_nth0(RowInd, ColInd, Matrix, Entry) :-
nth0(RowInd, Matrix, Row),
nth0(ColInd, Row, Entry).
Secondly, it can also help to use this predicate to define the entries relative to ones indexed by Row and Col in terms of directions, such as:
up_left_entry(R-C, Matrix, E) :-
PrevR is R - 1,
PrevC is C - 1,
get_matrix_entry_nth0(PrevR, PrevC, Matrix, E).
up_entry(R-C, Matrix, E) :-
PrevR is R - 1,
get_matrix_entry_nth0(PrevR, C, Matrix, E).
up_right_entry(R-C, Matrix, E) :-
PrevR is R - 1,
NextC is C + 1,
get_matrix_entry_nth0(PrevR, NextC, Matrix, E).
right_entry(R-C, Matrix, E) :-
NextC is C + 1,
get_matrix_entry_nth0(R, NextC, Matrix, E).
down_right_entry(R-C, Matrix, E) :-
NextR is R + 1,
NextC is C + 1,
get_matrix_entry_nth0(NextR, NextC, Matrix, E).
down_entry(R-C, Matrix, E) :-
NextR is R + 1,
get_matrix_entry_nth0(NextR, C, Matrix, E).
down_left_entry(R-C, Matrix, E) :-
NextR is R + 1,
PrevC is C - 1,
get_matrix_entry_nth0(NextR, PrevC, Matrix, E).
left_entry(R-C, Matrix, E) :-
PrevC is C - 1,
get_matrix_entry_nth0(R, PrevC, Matrix, E).
With these auxiliary predicates in place, the solution is fairly straightforward:
matrix_path([R|Rs], P) :-
% determine rows, cols
length([R|Rs], Rows),
length(R, Cols),
% defer to matrix_path/4 to compute all paths starting with 0,0
matrix_path(0-0, Rows-Cols, [R|Rs], P).
matrix_path/2 is the entry-point to the program. This single-clause predicate pre-determines the number of rows and columns in the given matrix, and defers processing to matrix_path/4 which starts computation from the 0,0 (top-left) element.
matrix_path(R-C, Rows-Cols, Matrix, P) :-
C >= Cols, !,
% end of column, proceed to next row
NextRow is R + 1,
NextRow < Rows,
matrix_path(NextRow-0, Rows-Cols, Matrix, P).
The first clause of matrix_path/4 checks if the number of columns has been exceeded; if so, a cut (!) is used to exclude the clauses below, and re-starts the computation in the next row and resets the column index to 0.
matrix_path(R-C, _, Matrix, adj(S, T)) :-
% get this entry
get_matrix_entry_nth0(R, C, Matrix, S),
% get each adjacent entry
( up_left_entry(R-C, Matrix, T)
; up_entry(R-C, Matrix, T)
; up_right_entry(R-C, Matrix, T)
; right_entry(R-C, Matrix, T)
; down_right_entry(R-C, Matrix, T)
; down_entry(R-C, Matrix, T)
; down_left_entry(R-C, Matrix, T)
; left_entry(R-C, Matrix, T)
).
The second clause of matrix_path/4 simply tries to retrieve all possible 'paths' from the given entry. Some may fail (such as looking up in the top row), but Prolog will backtrack to find all solutions that work.
matrix_path(R-C, Rows-Cols, Matrix, P) :-
% get the entry for the next column in this row
NextC is C + 1,
matrix_path(R-NextC, Rows-Cols, Matrix, P).
The last clause of matrix_path/4 simply shifts processing relative to the item in the next column in the same row.
Running a simple example:
?- matrix_path([[1,2],[3,4]], P).
P = adj(1, 2) ;
P = adj(1, 4) ;
P = adj(1, 3) ;
P = adj(2, 4) ;
P = adj(2, 3) ;
P = adj(2, 1) ;
P = adj(3, 1) ;
P = adj(3, 2) ;
P = adj(3, 4) ;
P = adj(4, 1) ;
P = adj(4, 2) ;
P = adj(4, 3) ;
false.
Note that if you want all adjacent pairs immediately without backtracking, wrap the call in a findall/2, like this:
?- findall(P, matrix_path([[1,2],[3,4]], P), Ps).
Ps = [adj(1, 2), adj(1, 4), adj(1, 3), adj(2, 4), adj(2, 3), adj(2, 1), adj(3, 1), adj(3, 2), adj(..., ...)|...].
Enumeration of a finite set can be done easily using Prolog backtracking:
adjacent_pos((R,C), (Ra,Ca)) :-
(R > 1, Ra is R-1 ; Ra is R ; Ra is R+1),
(C > 1, Ca is C-1 ; Ca is C ; Ca is C+1),
once(Ra \= R ; Ca \= C).
Using paired nth1/3 we can access a cell:
cell(Mat, (R,C), Cell) :-
nth1(R, Mat, Row),
nth1(C, Row, Cell).
So, in a matrix NxM of distinct elements:
adjacent(Mat, Elem, Adj) :-
cell(Mat, Pe, Elem),
adjacent_pos(Pe, Pa),
cell(Mat, Pa, Adj).
simple test:
test :-
M = [[1,2,3],
[5,4,6],
[8,9,7]],
findall(A, adjacent(M,1,A), L1), writeln(L1),
findall(A, adjacent(M,4,A), L4), writeln(L4).
yields:
?- test.
[2,5,4]
[1,2,3,5,6,8,9,7]
true.
Note that the test R > 1 and C > 1 could be avoided, demanding to nth1/3 failure the 'out of band' test. The same is true for the upper limit, omitted indeed, with the added benefit that we are not limited to a predefined matrix size.
For some reason, this is not working. I am getting:
ERROR: is/2: Arguments are not sufficiently instantiated
1 add_list([]).
2 add_list([H|T]):-
3 Sum2 is Sum1 + H,
4 add_list(T).
I am trying to add the contents of a list (containing only numbers).
I'm not sure what you are trying to do. But if you are trying to calc total sum it will be this way (changed name to list_sum as add_list doesn't make any sense):
list_sum([], 0).
list_sum([H|T], Sum):-
list_sum(T, SubSum),
Sum is SubSum + H.
You can have a "functionnal mind" with foldl :
foldl(_P, [], V, V).
foldl(P, [H|T], V1, VF) :-
call(P, H, V1, V2),
foldl(P, T, V2, VF).
sum_list(L, S) :-
foldl(add, L, 0, S).
add(X, Y, Z) :-
Z is X+Y.
Alternatively you could also use an accumulator (the advantage is, that it is tail-recursive and therefore can be optimized)
list_sum(L,R) :- list_sum(L,0,R).
list_sum([],A,A).
list_sum([H|T],A,R) :- A1 is A + H, list_sum(T,A1,R).