Inverse factorial in Prolog - prolog

Can someone helping me to find a way to get the inverse factorial in Prolog...
For example inverse_factorial(6,X) ===> X = 3.
I have been working on it a lot of time.
I currently have the factorial, but i have to make it reversible. Please help me.

Prolog's predicates are relations, so once you have defined factorial, you have implicitly defined the inverse too. However, regular arithmetics is moded in Prolog, that is, the entire expression in (is)/2 or (>)/2 has to be known at runtime, and if it is not, an error occurs. Constraints overcome this shortcoming:
:- use_module(library(clpfd)).
n_factorial(0, 1).
n_factorial(N, F) :-
N #> 0, N1 #= N - 1, F #= N * F1,
n_factorial(N1, F1).
This definition now works in both directions.
?- n_factorial(N,6).
N = 3
; false.
?- n_factorial(3,F).
F = 6
; false.
Since SICStus 4.3.4 and SWI 7.1.25 also the following terminates:
?- n_factorial(N,N).
N = 1
; N = 2
; false.
See the manual for more.

For reference, here is the best implementation of a declarative factorial predicate I could come up with.
Two main points are different from #false's answer:
It uses an accumulator argument, and recursive calls increment the factor we multiply the factorial with, instead of a standard recursive implementation where the base case is 0. This makes the predicate much faster when the factorial is known and the initial number is not.
It uses if_/3 and (=)/3 extensively, from module reif, to get rid of unnecessary choice points when possible. It also uses (#>)/3 and the reified (===)/6 which is a variation of (=)/3 for cases where we have two couples that can be used for the if -> then part of if_.
factorial/2
factorial(N, F) :-
factorial(N, 0, 1, F).
factorial(N, I, N0, F) :-
F #> 0,
N #>= 0,
I #>= 0,
I #=< N,
N0 #> 0,
N0 #=< F,
if_(I #> 2,
( F #> N,
if_(===(N, I, N0, F, T1),
if_(T1 = true,
N0 = F,
N = I
),
( J #= I + 1,
N1 #= N0*J,
factorial(N, J, N1, F)
)
)
),
if_(N = I,
N0 = F,
( J #= I + 1,
N1 #= N0*J,
factorial(N, J, N1, F)
)
)
).
(#>)/3
#>(X, Y, T) :-
zcompare(C, X, Y),
greater_true(C, T).
greater_true(>, true).
greater_true(<, false).
greater_true(=, false).
(===)/6
===(X1, Y1, X2, Y2, T1, T) :-
( T1 == true -> =(X1, Y1, T)
; T1 == false -> =(X2, Y2, T)
; X1 == Y1 -> T1 = true, T = true
; X1 \= Y1 -> T1 = true, T = false
; X2 == Y2 -> T1 = false, T = true
; X2 \= Y2 -> T1 = false, T = false
; T1 = true, T = true, X1 = Y1
; T1 = true, T = false, dif(X1, Y1)
).
Some queries
?- factorial(N, N).
N = 1 ;
N = 2 ;
false. % One could probably get rid of the choice point at the cost of readability
?- factorial(N, 1).
N = 0 ;
N = 1 ;
false. % Same
?- factorial(10, N).
N = 3628800. % No choice point
?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000)).
% 79,283 inferences, 0.031 CPU in 0.027 seconds (116% CPU, 2541106 Lips)
N = 100. % No choice point
?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518284253697920827223758251185210916864000000000000000000000000)).
% 78,907 inferences, 0.031 CPU in 0.025 seconds (125% CPU, 2529054 Lips)
false.
?- F #> 10^100, factorial(N, F).
F = 11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000,
N = 70 ;
F = 850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000,
N = 71 ;
F = 61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000,
N = 72 ;
...

a simple 'low tech' way: enumerate integers until
you find the sought factorial, then 'get back' the number
the factorial being built is greater than the target. Then you can fail...
Practically, you can just add 2 arguments to your existing factorial implementation, the target and the found inverse.

Just implement factorial(X, XFact) and then swap arguments
factorial(X, XFact) :- f(X, 1, 1, XFact).
f(N, N, F, F) :- !.
f(N, N0, F0, F) :- succ(N0, N1), F1 is F0 * N1, f(N, N1, F1, F).

Related

Argument is not instantiated, need it to start at zero but also be able to change it

Whenever I run my code, I get an error that the arguments are not instantiated.
ads(X,Z):- mod(X,2) =:= 0, Z is Z+X.
ads(X,Z) :- mod(N,2) =\= 0,Z is Z.
sum_of_nums(0,0,0).
sum_of_nums(X,Y,Z) :- X=<Y, ad(X,Z), sum_of_nums(X+1,Y,Z).
I want to be able to add numbers from X to Y but only the even ones. Don't know why it doesn't work.
First, there are some tiny errors, your compiler should indicate, like the unnecessary singleton variables in the second clause. After repairing this, and replacing ads by ad we have:
ad(X,Z):- mod(X,2) =:= 0, Z is Z+X.
ad(X,Z) :- mod(X,2) =\= 0,Z is Z.
sum_of_nums(0,0,0).
sum_of_nums(X,Y,Z) :- X=<Y, ad(X,Z), sum_of_nums(X+1,Y,Z).
?- sum_of_nums(1,2,S).
error(instantiation_error,(is)/2).
To locate this error, I will insert some goals false such that the
resulting program still produces this instantiation error.
ad(X,Z):- mod(X,2) =:= 0, Z is Z+X, false.
ad(X,Z) :- false, mod(X,2) =\= 0,Z is Z.
sum_of_nums(0,0,0) :- false.
sum_of_nums(X,Y,Z) :- X=<Y, ad(X,Z), false, sum_of_nums(X+1,Y,Z).
?- sum_of_nums(1,2,S).
error(instantiation_error,(is)/2).
Therefore, you have an error in this part already.
It's the Z is Z+X. On the right hand side of (is)/2 you always
need to have variables that are instantiated (known). And Z is not
known.
Variables are a bit different in Prolog. You cannot reassign them.
And also, writing this in Prolog directly doesn't really show what the
language is good at.
sum_of(X, Y, 0) :-
X > Y.
sum_of(X1, Y, Z1) :-
X1 =< Y,
( X1 mod 2 =:= 0 -> S = X1 ; S = 0 ),
X2 is X1+1,
sum_of(X2, Y, Z2),
Z1 is Z2+S.
A more idiomatic way would be:
?- between(1,6,N).
N = 1
; N = 2
; N = 3
; N = 4
; N = 5
; N = 6.
?- between(1,6,N), N mod 2 =:= 0.
N = 2
; N = 4
; N = 6.
?- findall(N, ( between(1,6,N), N mod 2 =:= 0 ), Ns).
Ns = [2,4,6].
?- findall(N, ( between(1,6,N), N mod 2 =:= 0 ), Ns), sum_list(Ns,Sum).
Ns = [2,4,6], Sum = 12.
sum_of(X,Y,Sum) :-
findall(N, ( between(X,Y,N), N mod 2 =:= 0 ), Ns),
sum_list(Ns,Sum).

Prolog - Finding the Nth Fibonacci number using accumulators

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.

CLPFD constraint: is a prime number

I'm not even sure if this is possible, but I'm trying to write a predicate prime/1 which constrains its argument to be a prime number.
The problem I have is that I haven't found any way of expressing “apply that constraint to all integers less than the variable integer”.
Here is an attempt which doesn't work:
prime(N) :-
N #> 1 #/\ % Has to be strictly greater than 1
(
N #= 2 % Can be 2
#\/ % Or
(
N #> 2 #/\ % A number strictly greater than 2
N mod 2 #= 1 #/\ % which is odd
K #< N #/\
K #> 1 #/\
(#\ (
N mod K #= 0 % A non working attempt at expressing:
“there is no 1 < K < N such that K divides N”
))
)
).
I hoped that #\ would act like \+ and check that it is false for all possible cases but this doesn't seem to be the case, since this implementation does this:
?- X #< 100, prime(X), indomain(X).
X = 2 ; % Correct
X = 3 ; % Correct
X = 5 ; % Correct
X = 7 ; % Correct
X = 9 ; % Incorrect ; multiple of 3
X = 11 ; % Correct
X = 13 ; % Correct
X = 15 % Incorrect ; multiple of 5
…
Basically this unifies with 2\/{Odd integers greater than 2}.
EDIT
Expressing that a number is not prime is very easy:
composite(N) :-
I #>= J,
J #> 1,
N #= I*J.
Basically: “N is composite if it can be written as I*J with I >= J > 1”.
I am still unable to “negate” those constraints. I have tried using things like #==> (implies) but this doesn't seem to be implification at all! N #= I*J #==> J #= 1 will work for composite numbers, even though 12 = I*J doesn't imply that necessarily J = 1!
prime/1
This took me quite a while and I'm sure it's far from being very efficient but this seems to work, so here goes nothing:
We create a custom constraint propagator (following this example) for the constraint prime/1, as such:
:- use_module(library(clpfd)).
:- multifile clpfd:run_propagator/2.
prime(N) :-
clpfd:make_propagator(prime(N), Prop),
clpfd:init_propagator(N, Prop),
clpfd:trigger_once(Prop).
clpfd:run_propagator(prime(N), MState) :-
(
nonvar(N) -> clpfd:kill(MState), prime_decomposition(N, [_])
;
clpfd:fd_get(N, ND, NL, NU, NPs),
clpfd:cis_max(NL, n(2), NNL),
clpfd:update_bounds(N, ND, NPs, NL, NU, NNL, NU)
).
If N is a variable, we constrain its lower bound to be 2, or keep its original lower bound if it is bigger than 2.
If N is ground, then we check that N is prime, using this prime_decomposition/2 predicate:
prime_decomposition(2, [2]).
prime_decomposition(N, Z) :-
N #> 0,
indomain(N),
SN is ceiling(sqrt(N)),
prime_decomposition_1(N, SN, 2, [], Z).
prime_decomposition_1(1, _, _, L, L) :- !.
prime_decomposition_1(N, SN, D, L, LF) :-
(
0 #= N mod D -> !, false
;
D1 #= D+1,
(
D1 #> SN ->
LF = [N |L]
;
prime_decomposition_2(N, SN, D1, L, LF)
)
).
prime_decomposition_2(1, _, _, L, L) :- !.
prime_decomposition_2(N, SN, D, L, LF) :-
(
0 #= N mod D -> !, false
;
D1 #= D+2,
(
D1 #> SN ->
LF = [N |L]
;
prime_decomposition_2(N, SN, D1, L, LF)
)
).
You could obviously replace this predicate with any deterministic prime checking algorithm. This one is a modification of a prime factorization algorithm which has been modified to fail as soon as one factor is found.
Some queries
?- prime(X).
X in 2..sup,
prime(X).
?- X in -100..100, prime(X).
X in 2..100,
prime(X).
?- X in -100..0, prime(X).
false.
?- X in 100..200, prime(X).
X in 100..200,
prime(X).
?- X #< 20, prime(X), indomain(X).
X = 2 ;
X = 3 ;
X = 5 ;
X = 7 ;
X = 11 ;
X = 13 ;
X = 17 ;
X = 19.
?- prime(X), prime(Y), [X, Y] ins 123456789..1234567890, Y-X #= 2, indomain(Y).
X = 123457127,
Y = 123457129 ;
X = 123457289,
Y = 123457291 ;
X = 123457967,
Y = 123457969
…
?- time((X in 123456787654321..1234567876543210, prime(X), indomain(X))).
% 113,041,584 inferences, 5.070 CPU in 5.063 seconds (100% CPU, 22296027 Lips)
X = 123456787654391 .
Some problems
This constraint does not propagate as strongly as it should. For example:
?- prime(X), X in {2,3,8,16}.
X in 2..3\/8\/16,
prime(X).
when we should know that 8 and 16 are not possible since they are even numbers.
I have tried to add other constraints in the propagator but they seem to slow it down more than anything else, so I'm not sure if I was doing something wrong or if it is slower to update constaints than check for primeness when labeling.

How to find the biggest digit in a number in Prolog?

I have an easy task, but somehow I haven't solved it in over an hour. This recursion I am doing isn't working, I'm stuck in an infinte loop. It should compare the last digit of a number with every other and remember the biggest one. Would really like to know why is my logic faulty and how to solve this problem.
This is my try on it:
maxDigit(X,X):-
X<10.
maxDigit(X,N):-
X1 is X//10,
X2 is X mod 10,
maxDigit(X1,N1),
X2=<N1,
N is N1.
maxDigit(X,N):-
X1 is X//10,
X2 is X mod 10,
maxDigit(X1,N1),
X2>N1,
N is X2.
Using SICStus Prolog 4.3.3 we simply combine n_base_digits/3 and maximum/2 like so:
?- n_base_digits(12390238464, 10, _Digits), maximum(Max, _Digits).
Max = 9.
A comment suggested stopping as soon as the maximum digit is encountered. This is how we do:
:- use_module(library(clpfd)).
:- use_module(library(reif)).
#=(X, Y, T) :- X #= Y #<==> B, bool10_t(B, T).
bool10_t(1, true).
bool10_t(0,false).
Based on if_/3, (;)/3 and (#=)/3 we then define:
n_base_maxdigit(N, Base, D) :-
N #> 0, % positive integers only
Base #> 1, % smallest base = 2
D #>= 0,
D #< Base,
n_base_maxdigit0_maxdigit(N, Base, 0, D).
n_base_maxdigit0_maxdigit(N, Base, D0, D) :-
D1 #= N mod Base,
N0 #= N // Base,
D2 #= max(D0,D1),
if_(( D2 + 1 #= Base ; N0 #= 0 ),
D = D2,
n_base_maxdigit0_maxdigit(N0, Base, D2, D)).
Sample query using SWI-Prolog 7.3.22 with Prolog lambda:
?- use_module(library(lambda)).
true.
?- Max+\ ( N is 7^7^7 * 10+9, time(n_base_maxdigit(N,10,Max)) ).
% 663 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 1022162 Lips)
Max = 9.
You have just to use if/then/else :
maxDigit(X,X):-
X<10,
!. % added after false's remark
maxDigit(X,N):-
X1 is X//10,
X2 is X mod 10,
maxDigit(X1,N1),
( X2<N1
-> N = N1
; N = X2).
in SWI-Prolog could be:
maxDigit(N,M) :- number_codes(N,L), max_list(L,T), M is T-0'0.

Factors of a number

So I am relatively new to Prolog, and while this problem is easy in many other languages I am having a lot of trouble with it. I want to generate a List of factors for a number N. I have already built a predicate that tells me if a number is a factor:
% A divides B
% A is a factor of B
divides(A,B) :- A =\= 0, (B mod A) =:= 0.
% special case where 1 // 2 would be 0
factors(1,[1]) :- !.
% general case
factors(N,L):- N > 0, factor_list(1, N, L).
factor_list(S,E,L) :- S =< E // 2, f_list(S,E,L).
f_list(S,E,[]) :- S > E // 2, !.
f_list(S,E,[S|T]) :- divides(S,E), !, S1 is S+1, f_list(S1, E, T).
f_list(S,E,L) :- S1 is S+1, f_list(S1,E,L).
Any help would be appreciated.
EDIT
I pretty much changed my entire solution, but for some reason predicates like factors(9, [1]) return true, when I only want factors(9, [1,3]) to return true. Any thoughts?
Here's why factors(9,[1]) is true: the timing of attempted instantiations (that is to say, unifications) is off:
f_list(S,E,[]) :- S > E // 2, !.
f_list(S,E,[S|T]) :- divides(S,E), !, S1 is S+1, f_list(S1, E, T).
f_list(S,E,L) :- S1 is S+1, f_list(S1,E,L).
%% flist(1,9,[1]) -> (2nd clause) divides(1,9), S1 is 2, f_list(2,9,[]).
%% flist(2,9,[]) -> (3rd clause) S1 is 3, f_list(3,9,[]).
%% ...
%% flist(5,9,[]) -> (1st clause) 5 > 9 // 2, !.
because you pre-specify [1], when it reaches 3 the tail is [] and the match with the 2nd clause is prevented by this, though it would succeed due to divides/2.
The solution is to move the unifications out of clauses' head into the body, and make them only at the appropriate time, not sooner:
f_list(S,E,L) :- S > E // 2, !, L=[].
f_list(S,E,L) :- divides(S,E), !, L=[S|T], S1 is S+1, f_list(S1, E, T).
f_list(S,E,L) :- S1 is S+1, f_list(S1,E,L).
The above usually is written with the if-else construct:
f_list(S,E,L) :-
( S > E // 2 -> L=[]
; divides(S,E) -> L=[S|T], S1 is S+1, f_list(S1, E, T)
; S1 is S+1, f_list(S1, E, L)
).
Also you can simplify the main predicate as
%% is not defined for N =< 0
factors(N,L):-
( N =:= 1 -> L=[1]
; N >= 2 -> f_list(1,N,L)
).
Personally, I use a somewhat simpler looking solution:
factors(1,[1]):- true, !.
factors(X,[Factor1|T]):- X > 0,
between(2,X,Factor1),
NewX is X // Factor1, (X mod Factor1) =:= 0,
factors(NewX,T), !.
This one only accepts an ordered list of the factors.
Here is a simple enumeration based procedure.
factors(M, [1 | L]):- factors(M, 2, L).
factors(M, X, L):-
residue(M, X, M1),
((M==M1, L=L1); (M1 < M, L=[X|L1])),
((M1=1, L1=[]); (M1 > X, X1 is X+1, factors(M1, X1, L1))).
residue(M, X, M1):-
((M < X, M1=M);
(M >=X, MX is M mod X,
(MX=0, MM is M/X, residue(MM, X, M1);
MX > 0, M1=M))).

Resources