Combining two numbers in prolog - prolog

Kindly, could you help me in the following:
I am writing a Prolog program that takes two numbers digits then combine them as one number, for example:
Num1: 5
Num2: 1
Then the new number is 51.
Assume V1 is the first number digit and V2 is the second number digit. I want to combine V1 and V2 then multiply the new number with V3, so my question is how I can do it?
calculateR(R, E, V1, V2, V3, V4):-
R is V1 V2 * V3,
E is R * V4.
Your help is appreciated.

Here is another solution that is based on the idea of #aBathologist and that relies on ISO predicates only, and does not dependent on SWI's idiosyncratic modifications and extensions. Nor does it have most probably unwanted solutions like calculateR('0x1',1,1,17). nor calculateR(1.0e+30,0,1,1.0e+300). Nor does it create unnecessary temporary atoms.
So the idea is to restrict the definition to decimal numbers:
digit_digit_number(D1, D2, N) :-
number_chars(D1, [Ch1]),
number_chars(D2, [Ch2]),
number_chars(N, [Ch1,Ch2]).

Here is a version which better clarifies the relational nature of Prolog - using library(clpfd) which is available in many Prolog systems (SICStus, SWI, B, GNU, YAP). It is essentially the same program as the one with (is)/2 except that I added further redundant constraints that permit the system to ensure termination in more general cases, too:
:- use_module(library(clpfd)).
digits_radix_number(Ds, R, N) :-
digits_radix_numberd(Ds, R, 0,N).
digits_radix_numberd([], _, N,N).
digits_radix_numberd([D|Ds], R, N0,N) :-
D #>= 0, D #< R,
R #> 0,
N0 #=< N,
N1 #= D+N0*R,
digits_radix_numberd(Ds, R, N1,N).
Here are some uses:
?- digits_radix_number([1,4,2],10,N).
N = 142.
?- digits_radix_number([1,4,2],R,142).
R = 10.
?- digits_radix_number([1,4,2],R,N).
R in 5..sup, 4+R#=_A, _A*R#=_B, _A in 9..sup, N#>=_A,
N in 47..sup, 2+_B#=N, _B in 45..sup.
That last query asks for all possible radices that represent [1,4,2] as a number. As you can see, not anything can be represented that way. The radix has to be 5 or larger which is not surprising given the digit 4, and the number itself has to be at least 47.
Let's say we want to get a value between 1450..1500, what radix do we need to do that?
?- digits_radix_number([1,4,2],R,N), N in 1450..1500.
R in 33..40, 4+R#=_A, _A*R#=_B, _A in 37..44,
N in 1450..1500, 2+_B#=N, _B in 1448..1498.
Gnah, again gibberish. This answer contains many extra equations that have to hold. Prolog essentially says: Oh yes, there is a solution, provided all this fine print is true. Do the math yourself!
But let's face it: It is better if Prolog gives such hard-to-swallow answer than if it would say Yes.
Fortunately there are ways to remove such extra conditions. One of the simplest is called "labeling", where Prolog will "try out" value after value:
?- digits_radix_number([1,4,2],R,N), N in 1450..1500, labeling([],[N]).
false.
That is clear response now! There is no solution. All these extra conditions where essentially false, like all that fine print in your insurance policy...
Here's another question: Given the radix and the value, what are the required digits?
?- digits_radix_number(D,10,142).
D = [1,4,2]
; D = [0,1,4,2]
; D = [0,0,1,4,2]
; D = [0,0,0,1,4,2]
; D = [0,0,0,0,1,4,2]
; ... .
So that query can never terminate, because 00142 is the same number as 142. Just as 007 is agent number 7.

Here is a straight-forward solution that should work in any Prolog close to ISO:
digits_radix_to_number(Ds, R, N) :-
digits_radix_to_number(Ds, R, 0,N).
digits_radix_to_number([], _, N,N).
digits_radix_to_number([D|Ds], R, N0,N) :-
N1 is D+N0*R,
digits_radix_to_number(Ds, R, N1,N).
?- digits_radix_to_number([1,4,2],10,R).
R = 142.

Edit: In a comment, #false pointed out that this answer is SWI-Prolog specific.
You can achieve your desired goal by treating the numerals as atoms and concatenating them, and then converting the resultant atom into a number.
I'll use atom_concat/3 to combine the two numerals. In this predicate, the third argument with be the combination of atoms in its first and second arguments. E.g.,
?- atom_concat(blingo, dingo, X).
X = blingodingo.
Note that, when you do this with two numerals, the result is an atom not a number. This is indicated by the single quotes enclosing the the result:
?- atom_concat(5, 1, X).
X = '51'.
But 51 \= '51' and we cannot multiply an atom by number. We can use atom_number/2 to convert this atom into a number:
?- atom_number('51', X).
X = 51.
That's all there is to it! Your predicate might look like this:
calculateR(No1, No2, Multiplier, Result) :-
atom_concat(No1, No2, NewNoAtom),
atom_number(NewNoAtom, NewNo),
Result is NewNo * Multiplier.
Usage example:
?- calculateR(5, 1, 3, X).
X = 153.
Of course, you'll need more if you want to prompt the user for input.
I expect #Wouter Beek's answer is more efficient, since it doesn't rely on converting the numbers to and from atoms, but just uses the assumption that each numeral is a single digit to determine the resulting number based on their position. E.g., if 5 is in the 10s place and 1 is in the 1s place, then the combination of 5 and 1 will be 5 * 10 + 1 * 1. The answer I suggest here will work with multiple digit numerals, e.g., in calculateR(12, 345, 3, Result), Result is 1234 * 3. Depending on what you're after this may or may not be a desired result.

If you know the radix of the numbers involved (and the radix is the same for all the numbers involved), then you can use the reverse index of the individual numbers in order to calculate their positional summation.
:- use_module(library(aggregate)).
:- use_module(library(lists)).
digits_to_number(Numbers1, Radix, PositionalSummation):-
reverse(Numbers1, Numbers2),
aggregate_all(
sum(PartOfNumber),
(
nth0(Position, Numbers2, Number),
PartOfNumber is Number * Radix ^ Position
),
PositionalSummation
).
Examples of use:
?- digits_to_number([5,1], 10, N).
N = 51.
?- digits_to_number([5,1], 16, N).
N = 81.
(The code sample is mainly intended to bring the idea across. Notice that I use aggregate_all/3 from SWI-Prolog here. The same could be achieved by using ISO predicates exclusively.)

Related

How can I verify if a coordinate is in a list

I'm generating random coordinates and adding on my list, but first I need verify if that coordinate already exists. I'm trying to use member but when I was debugging I saw that isn't working:
My code is basically this:
% L is a list and Q is a count that define the number of coordinate
% X and Y are the coordinate members
% check if the coordniate already exists
% if exists, R is 0 and if not, R is 1
createCoordinates(L,Q) :-
random(1,10,X),
random(1,10,Y),
convertNumber(X,Z),
checkCoordinate([Z,Y],L,R),
(R is 0 -> print('member'), createCoordinates(L,Q); print('not member'),createCoordinates(L,Q-1).
checkCoordinate(C,L,R) :-
(member(C,L) -> R is 0; R is 1).
% transforms the number N in a letter L
convertNumber(N,L) :-
N is 1, L = 'A';
N is 2, L = 'B';
...
N is 10, L = 'J'.
%call createCoordinates
createCoordinates(L,20).
When I was debugging this was the output:
In this picture I'm in the firts interation and L is empty, so R should be 1 but always is 0, the coordinate always is part of the list.
I have the impression that the member clause is adding the coordinate at my list and does'nt make sense
First off, I would recommend breaking your problem down into smaller pieces. You should have a procedure for making a random coordinate:
random_coordinate([X,Y]) :-
random(1, 10, XN), convertNumber(XN, X),
random(1, 10, Y).
Second, your checkCoordinate/3 is converting Prolog's success/failure into an integer, which is just busy work for Prolog and not really improving life for you. memberchk/2 is completely sufficient to your task (member/2 would work too but is more powerful than necessary). The real problem here is not that member/2 didn't work, it's that you are trying to build up this list parameter on the way out, but you need it to exist on the way in to examine it.
We usually solve this kind of problem in Prolog by adding a third parameter and prepending values to the list on the way through. The base case then equates that list with the outbound list and we protect the whole thing with a lower-arity procedure. In other words, we do this:
random_coordinates(N, Coordinates) :- random_coordinates(N, [], Coordinates).
random_coordinates(0, Result, Result).
random_coordinates(N, CoordinatesSoFar, FinalResult) :- ...
Now that we have two things, memberchk/2 should work the way we need it to:
random_coordinates(N, CoordinatesSoFar, FinalResult) :-
N > 0, succ(N0, N), % count down, will need for recursive call
random_coordinate(Coord),
(memberchk(Coord, CoordinatesSoFar) ->
random_coordinates(N, CoordinatesSoFar, FinalResult)
;
random_coordinates(N0, [Coord|CoordinatesSoFar], FinalResult)
).
And this seems to do what we want:
?- random_coordinates(10, L), write(L), nl.
[[G,7],[G,3],[H,9],[H,8],[A,4],[G,1],[I,9],[H,6],[E,5],[G,8]]
?- random_coordinates(10, L), write(L), nl.
[[F,1],[I,8],[H,4],[I,1],[D,3],[I,6],[E,9],[D,1],[C,5],[F,8]]
Finally, I note you continue to use this syntax: N is 1, .... I caution you that this looks like an error to me because there is no distinction between this and N = 1, and your predicate could be stated somewhat tiresomely just with this:
convertNumber(1, 'A').
convertNumber(2, 'B').
...
My inclination would be to do it computationally with char_code/2 but this construction is actually probably better.
Another hint that you are doing something wrong is that the parameter L to createCoordinates/2 gets passed along in all cases and is not examined in any of them. In Prolog, we often have variables that appear to just be passed around meaninglessly, but they usually change positions or are used multiple times, as in random_coordinates(0, Result, Result); while nothing appears to be happening there, what's actually happening is plumbing: the built-up parameter becomes the result value. Nothing interesting is happening to the variable directly there, but it is being plumbed around. But nothing is happening at all to L in your code, except it is supposedly being checked for a new coordinate. But you're never actually appending anything to it, so there's no reason to expect that anything would wind up in L.
Edit Notice that #lambda.xy.x solves the problem in their answer by prepending the new coordinate in the head of the clause and examining the list only after the recursive call in the body, obviating the need for the second list parameter.
Edit 2 Also take a look at #lambda.xy.x's other solution as it has better time complexity as N approaches 100.
Since i had already written it, here is an alternative solution: The building block is gen_coord_notin/2 which guarantees a fresh solution C with regard to an exclusion list Excl.
gen_coord_notin(C, Excl) :-
random(1,10,X),
random(1,10,Y),
( memberchk(X-Y, Excl) ->
gen_coord_notin(C, Excl)
;
C = X-Y
).
The trick is that we only unify C with the new result, if it is fresh.
Then we only have to fold the generations into N iterations:
gen_coords([], 0).
gen_coords([X|Xs], N) :-
N > 0,
M is N - 1,
gen_coords(Xs, M),
gen_coord_notin(X, Xs).
Remark 1: since coordinates are always 2-tuples, a list representation invites unwanted errors (e.g. writing [X|Y] instead of [X,Y]). Traditionally, an infix operator like - is used to seperate tuples, but it's not any different than using coord(X,Y).
Remark 2: this predicate is inherently non-logical (i.e. calling gen_coords(X, 20) twice will result in different substitutions for X). You might use the meta-level predicates var/1, nonvar/1, ground/1, integer, etc. to guard against non-sensical calls like gen_coord(1-2, [1-1]).
Remark 3: it is also important that the conditional does not have multiple solutions (compare member(X,[A,B]) and memberchk(X,[A,B])). In general, this can be achieved by calling once/1 but there is a specialized predicate memberchk/2 which I used here.
I just realized that the performance of my other solutions is very bad for N close to 100. The reason is that with diminishing possible coordinates, the generate and test approach will take longer and longer. There's an alternative solution which generates all coordinates and picks N random ones:
all_pairs(Ls) :-
findall(X-Y, (between(1,10,X), between(1,10,Y)), Ls).
remove_index(X,[X|Xs],Xs,0).
remove_index(I,[X|Xs],[X|Rest],N) :-
N > 0,
M is N - 1,
remove_index(I,Xs,Rest,M).
n_from_pool(_Pool, [], 0).
n_from_pool(Pool, [C|Cs], N) :-
N > 0,
M is N - 1,
length(Pool, L),
random(0,L,R),
remove_index(C,Pool,NPool,R),
n_from_pool(NPool, Cs, M).
gen_coords2(Xs, N) :-
all_pairs(Pool),
n_from_pool(Pool, Xs, N).
Now the query
?- gen_coords2(Xs, 100).
Xs = [4-6, 5-6, 5-8, 9-6, 3-1, 1-3, 9-4, 6-1, ... - ...|...] ;
false.
succeeds as expected. The error message
?- gen_coords2(Xs, 101).
ERROR: random/1: Domain error: not_less_than_one' expected, found0'
when we try to generate more distinct elements than possible is not nice, but better than non-termination.

Prolog permutations with condition?

I have this program to generate all the permutations of a list. The thing is, I need to generate only the permutations in which the consecutive terms have the absolute difference less or equal than 3. Something like:
[2,7,5] => [2,5,7] and [7,5,2]. [2 7 5] would be wrong since 2-7 = -5 and |-5| > 3
The permutation program:
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S).
permutfin(X,R):-
findall(P,perm(X,P),R).
I know I'm supposed to add the condition somewhere in the perm function but I can't figure out exactly what or where to write.
A more intuitive way to write a permutation is:
takeout([X|T],X,T).
takeout([H|L],X,[H|T]) :-
takeout(L,X,T).
Where the first element is the original list, the second the element picked, and the third the list without that element.
In that case the permutation predicate is defined as:
perm([],[]).
perm(L,[E|T]) :-
takeout(L,E,R),
perm(R,T).
this also allows tail-recursion which can imply an important optimization in most Prolog systems.
Now in order to generate only permutations with a consecutive difference of at most three, you can do two things:
The naive way is generate and test: here you let Prolog generate a permutation, but you only accept it if a certain condition is met. For instance:
dif3([_]).
dif3([A,B|T]) :-
D is abs(A-B),
D =< 3,
dif3([B|T]).
and then define:
perm3(L,R) :-
perm(L,R),
dif3(R).
This approach is not very efficient: it can be the case that for an exponential amount of permutations, only a few are valid, and this would imply a large computational effort. If for instance the list of elements is [2,5,7,9] it will generate all permutations starting with [2,9,...] while a more intelligent approach could already see that will never generate a valid solution anyway.
the other more intelligent approach is interleaved generate and test. Here you select only numbers with takeout3/4 that are valid candidates. You can define a predicate takeout3(L,P,X,T). where L is the original list, P the previous number, X the selected number and T the resulting list:
takeout3([X|T],P,X,T) :-
D is abs(X-P),
D =< 3.
takeout3([H|L],N,X,[H|T]) :-
takeout3(L,N,X,T).
Now we can generate a permutation as follows:
perm3([],[]).
perm3(L,[E|T]) :-
takeout(L,E,R),
perm3(R,E,T).
perm3([],_,[]).
perm3(L,O,[E|T]) :-
takeout3(L,O,E,R),
perm3(R,E,T).
Mind we use two versions of perm3: perm3/2 and perm3/3, the first is used to generate the first element (using the old takeout/3), and perm3/3 is used to generate the remainder of the permutation using takeout3/4.
The full source code of this approach is:
takeout([X|T],X,T).
takeout([H|L],X,[H|T]) :-
takeout(L,X,T).
takeout3([X|T],P,X,T) :-
D is abs(X-P),
D =< 3.
takeout3([H|L],N,X,[H|T]) :-
takeout3(L,N,X,T).
perm3([],[]).
perm3(L,[E|T]) :-
takeout(L,E,R),
perm3(R,E,T).
perm3([],_,[]).
perm3(L,O,[E|T]) :-
takeout3(L,O,E,R),
perm3(R,E,T).
Running it with swipl gives:
?- perm3([2,7,5],L).
L = [2, 5, 7] ;
L = [7, 5, 2] ;
false.
The expected behavior.
Here is another solution. I added the condition in takeout to make sure the adjacent items are within 3 of each other:
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
check(_,[]).
check(X,[H|_]) :-
D is X - H,
D < 4,
D > -4.
takeout(X,[X|R],R) :-
check(X,R).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S),
check(F,R).

Multiplying peano integers in swi-prolog

I am currently on the verge of getting mad trying to solve a simple "multiply peano integers" problem in Prolog.
Basic rules
A peano integer is defined as follows: 0 -> 0; 1 -> s(0); 2 -> s(s(0)) s(s(s(0) -> 3 etc.
The relation is to be defined as follows: multiply(N1,N2,R)
Where
N1 is the first peano integer (i.e. something like s(s(0)))
N2 is the second peano integer (i.e. something like s(s(0)))
R is the resulting new peano integer (like s(s(s(s(0))))
I am aware of the fact that Prolog provides basic arithmetic logic by default, but I am trying to implement basic arithmetic logic using peano integers.
As a multiplication is basically a repeated addition, I think it could look something like this:
Prolog attempts
%Addition
% Adds two peano integers 3+2: add(s(s(s(0))),s(s(0)),X). --> X = s(s(s(s(s(0)))))
add(X,0,X).
add(X,s(Y),s(Z)) :- add(X,Y,Z).
%Loop
%Loop by N
loop(0).
loop(N) :- N>0, NewN is N-1, loop(NewN).
The problem is that I am out of ideas how I can get prolog to run the loop N times based on the coefficient, adding the peano integers and building up the correct result. I'm confident that this is rather easy to achieve and that the resulting code probably won't be longer than a few lines of code. I've been trying to achieve this for hours now and it's starting to make me mad.
Thank you so much for your help, and ... Merry Christmas!
Mike
thanks #false for the hint to this post:
Prolog successor notation yields incomplete result and infinite loop
The referenced PDF doc in this post helps clarifying a number of features regarding peano integers and how to get simple arithmetic to work - pages 11 and 12 are particularly interesing: http://ssdi.di.fct.unl.pt/flcp/foundations/0910/files/class_02.pdf
The code could be set up like this - please note the two approaches for multiplying the integers:
%Basic assumptions
int(0). %0 is an integer
int(s(M)) :- int(M). %the successor of an integer is an integer
%Addition
sum(0,M,M). %the sum of an integer M and 0 is M.
sum(s(N),M,s(K)) :- sum(N,M,K). %The sum of the successor of N and M is the successor of the sum of N and M.
%Product
%Will work for prod(s(s(0)),s(s(0)),X) but not terminate for prod(X,Y,s(s(0)))
prod(0,M,0). %The product of 0 with any integer is 0
prod(s(N),M,P) :-
prod(N,M,K),
sum(K,M,P).%The product of the successor of N and M is the sum of M with the product of M and N. --> (N+1)*M = N*M + M
%Product #2
%Will work in both forward and backward direction, note the order of the calls for sum() and prod2()
prod2(0,_,0). %The product of 0 with any given integer is 0
prod2(s(N), M, P) :- % implements (N+1)*M = M + N*M
sum(M, K, P),
prod2(M,N,K).
Which, when consulting the database will give you something like this:
?- prod(s(s(s(0))),s(s(s(0))),Result).
Result = s(s(s(s(s(s(s(s(s(0))))))))).
?- prod2(s(s(s(0))),s(s(s(0))),Result).
Result = s(s(s(s(s(s(s(s(s(0))))))))).
Please note the different behavior of prod() and prod2() when consulting Prolog in reverse direction - when tracing, please pay attention to the way Prolog binds its variables during the recursive calls:
?- prod(F1,F2,s(s(s(s(0))))).
F1 = s(0),
F2 = s(s(s(s(0)))) ;
F1 = F2, F2 = s(s(0)) ;
ERROR: Out of global stack
?- prod2(F1,F2,s(s(s(s(0))))).
F1 = s(s(s(s(0)))),
F2 = s(0) ;
F1 = F2, F2 = s(s(0)) ;
F1 = s(0),
F2 = s(s(s(s(0)))) ;
false.
I would therefore discourage from the use of prod() as it doesn't reliably terminate in all thinkable scenarios and use prod2() instead.
I'm really excited by the people here at StackOverflow. I got so much useful feedback, which really helped me in getting a deeper understanding of how Prolog works. Thanks a ton everyone!
Mike
Edit: Had another look at this issue thanks to #false and the following post: Prolog successor notation yields incomplete result and infinite loop

Prevent backtracking after first solution to Fibonacci pair

The term fib(N,F) is true when F is the Nth Fibonacci number.
The following Prolog code is generally working for me:
:-use_module(library(clpfd)).
fib(0,0).
fib(1,1).
fib(N,F) :-
N #> 1,
N #=< F + 1,
F #>= N - 1,
F #> 0,
N1 #= N - 1,
N2 #= N - 2,
F1 #=< F,
F2 #=< F,
F #= F1 + F2,
fib(N1,F1),
fib(N2,F2).
When executing this query (in SICStus Prolog), the first (and correct) match is found for N (rather instantly):
| ?- fib(X,377).
X = 14 ?
When proceeding (by entering ";") to see if there are any further matches (which is impossible by definition), it takes an enormous amount of time (compared to the first match) just to always reply with no:
| ?- fib(X,377).
X = 14 ? ;
no
Being rather new to Prolog, I tried to use the Cut-Operator (!) in various ways, but I cannot find a way to prevent the search after the first match. Is it even possible given the above rules? If yes, please let me know how :)
There are two parts to get what you want.
The first is to use
call_semidet/1
which ensures that there is exactly one answer. See links for an
implementation for SICStus, too. In the unlikely event of having more
than one answer, call_semidet/1 produces a safe error. Note that
once/1 and !/0 alone simply cut away whatever there has been.
However, you will not be very happy with call_semidet/1 alone. It
essentially executes a goal twice. Once to see if there is no more
than one answer, and only then again to obtain the first answer. So
you will get your answer much later.
The other part is to speed up your definition such that above will not
be too disturbing to you. The solution suggested by CapelliC changes
the algorithm altogether which is specific to your concrete function
but does not extend to any other function. But it also describes a
different relation.
Essentially, you found the quintessential parts already, you only need
to assemble them a bit differently to make them work. But, let's start
with the basics.
CLPFD as CLP(Z)
What you are doing here is still not that common to many Prolog
programmers. You use finite domain constraints for general integer
arithmetics. That is, you are using CLPFD as a pure substitute to the
moded expression evaluation found in (is)/2, (>)/2 and the
like. So you want to extend the finite domain paradigm which assumes
that we express everything within finite given intervals. In fact, it
would be more appropriate to call this extension CLP(Z).
This extension does not work in every Prolog offering finite
domains. In fact, there is only SICStus, SWI and YAP that correctly
handle the case of infinite intervals. Other systems might fail or
succeed when they rather should succeed or fail - mostly when integers
are getting too large.
Understanding non-termination
The first issue is to understand the actual reason why your original
program did not terminate. To this end, I will use a failure
slice. That
is, I add false goals into your program. The point being: if the
resulting program does not terminate then also the original program
does not terminate. So the minimal failure slice of your (presumed)
original program is:
fiborig(0,0) :- false.
fiborig(1,1) :- false.
fiborig(N,F) :-
N #> 1,
N1 #= N-1,
N2 #= N-2,
F #= F1+F2,
fiborig(N1,F1), false,
fiborig(N2,F2).
There are two sources for non-termination here: One is that for a given
F there are infinitely many values for F1 and F2. That can be
easily handled by observing that F1 #> 0, F2 #>= 0.
The other is more related to Prolog's execution mechanism. To
illustrate it, I will add F2 #= 0. Again, because the resulting
program does not terminate, also the original program will loop.
fiborig(0,0) :- false.
fiborig(1,1) :- false.
fiborig(N,F) :-
N #> 1,
N1 #= N-1,
N2 #= N-2,
F #= F1+F2,
F1 #> 0,
F2 #>= 0,
F2 #= 0,
fiborig(N1,F1), false,
fiborig(N2,F2).
So the actual problem is that the goal that might have 0 as result
is executed too late. Simply exchange the two recursive goals. And add
the redundant constraint F2 #=< F1 for efficiency.
fibmin(0,0).
fibmin(1,1).
fibmin(N,F) :-
N #> 1,
N1 #= N-1,
N2 #= N-2,
F1 #> 0,
F2 #>= 0,
F1 #>= F2,
F #= F1+F2,
fibmin(N2,F2),
fibmin(N1,F1).
On my lame laptop I got the following runtimes for fib(N, 377):
SICStus SWI
answer/total
fiborig: 0.090s/27.220s 1.332s/1071.380s
fibmin: 0.080s/ 0.110s 1.201s/ 1.506s
Take the sum of both to get the runtime for call_semidet/1.
Note that SWI's implementation is written in Prolog only, whereas
SICStus is partly in C, partly in Prolog. So when porting SWI's (actually #mat's) clpfd to
SICStus, it might be comparable in speed.
There are still many things to optimize. Think of indexing, and the
handling of the "counters", N, N1, N2.
Also your original program can be improved quite a bit. For example,
you are unnecessarily posting the constraint F #>= N-1 three times!
If you are only interested in the first solution or know that there is at most one solution, you can use once/1 to commit to that solution:
?- once(fib(X, 377)).
+1 for using CLP(FD) as a declarative alternative to lower-level arithmetic. Your version can be used in all directions, whereas a version based on primitive integer arithmetic cannot.
I played a bit with another definition, I wrote in standard arithmetic and translated to CLP(FD) on purpose for this question.
My plain Prolog definition was
fibo(1, 1,0).
fibo(2, 2,1).
fibo(N, F,A) :- N > 2, M is N -1, fibo(M, A,B), F is A+B.
Once translated, since it take too long in reverse mode (or doesn't terminate, don't know),
I tried to add more constraints (and moving them around) to see where a 'backward' computation terminates:
fibo(1, 1,0).
fibo(2, 2,1).
fibo(N, F,A) :-
N #> 2,
M #= N -1,
M #>= 0, % added
A #>= 0, % added
B #< A, % added - this is key
F #= A+B,
fibo(M, A,B). % moved - this is key
After adding B #< A and moving the recursion at last call, now it works.
?- time(fibo(U,377,Y)).
% 77,005 inferences, 0.032 CPU in 0.033 seconds (99% CPU, 2371149 Lips)
U = 13,
Y = 233 ;
% 37,389 inferences, 0.023 CPU in 0.023 seconds (100% CPU, 1651757 Lips)
false.
edit To account for 0 based sequences, add a fact
fibo(0,0,_).
Maybe this explain the role of the last argument: it's an accumulator.

Trying to count steps through recursion?

This is a cube, the edges of which are directional; It can only go left to right, back to front and top to bottom.
edge(a,b).
edge(a,c).
edge(a,e).
edge(b,d).
edge(b,f).
edge(c,d).
edge(c,g).
edge(d,h).
edge(e,f).
edge(e,g).
edge(f,h).
edge(g,h).
With the method below we can check if we can go from A-H for example: cango(A,H).
move(X,Y):- edge(X,Y).
move(X,Y):- edge(X,Z), move(Z,Y).
With move2, I'm trying to impalement counting of steps required.
move2(X,Y,N):- N is N+1, edge(X,Y).
move2(X,Y,N):- N is N+1, edge(X,Z), move2(Z,Y,N).
How would I implement this?
arithmetic evaluation is carried out as usual in Prolog, but assignment doesn't work as usual. Then you need to introduce a new variable to increment value:
move2(X,Y,N,T):- T is N+1, edge(X,Y).
move2(X,Y,N,T):- M is N+1, edge(X,Z), move2(Z,Y,M,T).
and initialize N to 0 at first call. Such added variables (T in our case) are often called accumulators.
move2(X,Y,1):- edge(X,Y), ! .
move2(X,Y,NN):- edge(X,Z), move2(Z,Y,N), NN is N+1 .
(is)/2 is very sensitive to instantiations in its second argument. That means that you cannot use it in an entirely relational manner. You can ask X is 1+1., you can even ask 2 is 1+1. but you cannot ask: 2 is X+1.
So when you are programming with predicates like (is)/2, you have to imagine what modes a predicate will be used with. Such considerations easily lead to errors, in particular, if you just started. But don't worry, also more proficient programmers still fall prey to such problems.
There is a clean alternative in several Prolog systems: In SICStus, YAP, SWI there is a library(clpfd) which permits you to express relations between integers. Usually this library is used for constraint programming, but you can also use it as a safe and clean replacement for (is)/2 on the integers. Even more so, this library is often very efficiently compiled such that the resulting code is comparable in speed to (is)/2.
?- use_module(library(clpfd)).
true.
?- X #= 1+1.
X = 2.
?- 2 #= 1+1.
true.
?- 2 #= X+1.
X = 1.
So now back to your program, you can simply write:
move2(X,Y,1):- edge(X,Y).
move2(X,Y,N0):- N0 #>= 1, N0 #= N1+1, edge(X,Z), move2(Z,Y,N1).
You get now all distances as required.
But there is more to it ...
To make sure that move2/3 actually terminates, try:
?- move2(A, B, N), false.
false.
Now we can be sure that move2/3 always terminates. Always?
Assume you have added a further edge:
edge(f, f).
Now above query loops. But still you can use your program to your advantage!
Determine the number of nodes:
?- setof(C,A^B^(edge(A,B),member(C,[A,B])),Cs), length(Cs, N).
Cs = [a, b, c, d, e, f, g, h], N = 8.
So the longest path will take just 7 steps!
Now you can ask the query again, but now by constraining N to a value less than or equal to7:
?- 7 #>= N, move2(A,B, N), false.
false.
With this additional constraint, you have again a terminating definition! No more loops.

Resources