Prime factorization in prolog - prolog

I'm new to Prolog. I read this code which finds prime factors of an integer:
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), !.
And changed it to this one:
factors(1,[[1,1]]) :-
true, !.
factors(X,[Factor1|T]) :-
X > 0,
( is_list(Factor1),
length(Factor1, 2),
Factor1 = [Base|A],
A = [Pow],
between(2,X,Base),
between(1,100,Pow),
NewX is X / (Base ** Pow),
(X mod Base) =:= 0,
(NewX mod Base) =\= 0
),
factors(NewX,T), !.
Well the first one works perfect, but the latter doesn't respond to queries. i.e. when I enter:
factors(2,[[2,1],[1,1]]).
I get 'true', but when I enter:
factors(2,X).
I get 'false'.

Because Base and Pow aren't bound to anything yet (they are parts of the X that you pass), you can't compute NewX (and the betweens might not work, either).

When you enter factors(2,X), Factor1 is not bound and is_list(Factor1) fails.
I think your code
is_list(Factor1),
length(Factor1, 2),
Factor1 = [Base|A],
A = [Pow]
can be abbreviated to Factor1 = [Base,Pow]. Since Factor1 is not used anywhere else, you can move [Base,Pow] into the head of the clause.
You can omit true, it has no effect here. The parenthesis haven't any effect, neither. So your code could be written as:
factors(1,[[1,1]]) :- !.
factors(X,[[Base,Pow]|T]) :-
X > 0,
between(2,X,Base),
between(1,100,Pow),
NewX is X / (Base ** Pow),
(X mod Base) =:= 0,
(NewX mod Base) =\= 0,
factors(NewX,T), !.
On my system (using SICStus), one must use
NewX is X // floor(Base ** Pow)
to prevent that NewX becomes a float which leads to an error when passed to mod as argument.
Edit: I originally wrote that the last cut had no effect. That is not true because between/2 creates choice points. I removed that part of my answer and put the cut back into the code.

Related

Basic primality test predicate in Prolog

I am trying to create a predicate isPrime/1 that checks if a given number is prime or not. I have come up with the following code:
primeRec(_, 2).
primeRec(X, Y) :- Y > 2, X mod Y-1 > 0, primeRec(X, Y-1).
isPrime(2).
isPrime(X) :- X > 1, X mod 2 > 0, primeRec(X, X).
but it does not seem to work, and I have no idea why. I've found my code to follow the same idea as this one here, except that mine always returns false. for any ?- isPrime(X). with X bigger than 2, which obviously should not happen.
The problem is that you need to define another variable, say Y1, and unify it with the evaluation of Y-1, i.e., Y1 is Y - 1 and use Y1 instead of Y-1 in the second rule for primeRec/1. This because if you want to evaluate an arithmetic expression you need to use is.
primeRec(X, Y) :- Y > 2, Y1 is Y - 1, X mod Y1 > 0, primeRec(X, Y1).

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).

List split in Prolog

Getting error in Prolog.
I want the 1st list to have positive numbers divisible by 2 and 2nd list to have negative number divisible by 3.
This is the code I've written so far but I'm getting the output as 'false'
split([],[],[]).
split([X|L],[X|L1],L2):- X >=0, X mod 2 =:= 0, split(L,L1,L2).
split([X|L],L1,[X|L2]):- X<0, X mod 3 =:= 0, split(L,L1,L2).
test case - split([1,-2,-3,4,-17,3],L1,L2).
output - false
Can someone please tell me where am I going wrong?
You did not specify a clause for values that are not positive and dividable by two, or negative and dividable by three. In that case you probably do not want to include it in any list, so:
split([],[],[]).
split([X|L],[X|L1],L2):-
X >= 0,
X mod 2 =:= 0,
split(L,L1,L2).
split([X|L],L1,[X|L2]):-
X < 0,
X mod 3 =:= 0,
split(L,L1,L2).
split([X|L], L1, L2) :-
\+ ((X >= 0, X mod 2 =:= 0); (X < 0, X mod 3 =:= 0)),
split(L, L1, L2).

Prolog - print the sum of the EVEN numbers from 1 to 10

I am trying to print all the even numbers from 1 to 10 using Prolog, and here is what I have tried:
printn(10,0):- write(10),!.
printn(X,Sum):-
( X mod 2 =:= 0 -> Sum is X+Sum, Next is X+1, nl, printn(Next);
Next is X+1, printn(Next) ).
but it returns false.
You don't need to create the list with the numbers from the beginning, it is better to examine numbers once:
print(X,Y):-print_even(X,Y,0).
print_even(X, X, Sum):-
( X mod 2 =:= 0 -> Sum1 is X+Sum;
Sum1 = Sum
), print(Sum1).
print_even(X, Y, Sum):-
X<Y, Next is X+1,
( X mod 2 =:= 0 -> Sum1 is X+Sum, print_even(Next, Y, Sum1);
print_even(Next, Y, Sum)
).
Keep in mind that in Prolog Sum is Sum+1 always fails you need to use a new variable e.g Sum1.
Example:
?- print(1,10).
30
true ;
false.
The most useful way of obtaining Prolog output is to capture the solution in a variable, either individually through backtracking, or in a list. The idea of "printing", which carries over from using other languages allows for formatting, etc, but is not considered the best way to express a solution.
In Prolog, you want to express your problem as a relation. For example, we might say, even_with_max(X, Max) is true (or succeeds) if X is an even number less than or equal to Max. In Prolog, when reasoning with integers, the CLP(FD) library is what you want to use.
:- use_module(library(clpfd)).
even_up_to(X, Max) :-
X in 1..Max,
X mod 2 #= 0, % EDIT: as suggested by Taku
label([X]).
This will yield:
3 ?- even_up_to(X, 10).
X = 2 ;
X = 4 ;
X = 6 ;
X = 8 ;
X = 10.
If you then want to collect into a list, you can use: findall(X, even_up_to(X), Evens).
What error do you have? Here is my solution:
Create list [1...10]
Filter it, excluding odd numbers
Sum elements of the list
Code:
sumList([], 0).
sumList([Head|Tail], Sum) :-
sumList(Tail, Rest),
Sum is Head + Rest.
isOdd(X) :-
not((X mod 2) =:= 0).
sumOfEvenNums(A, B, Out) :-
numlist(A, B, Numbers),
exclude(isOdd, Numbers, Even_numbers),
sumList(Even_numbers, Out).
Now you can call sumOfEvenNums(1, 10, N)
In ECLiPSe, you can write with iterator:
sum_even(Sum):-
( for(I,1,10),fromto(0,In,Out,Sum)
do
(I mod 2 =:= 0 -> Out is In + I;Out is In)
)
With library(aggregate):
evens_upto(Sum) :-
aggregate(sum(E), (between(1, 10, E), E mod 2 =:= 0), Sum).
Thanks to #CapelliC for the inspiration.

Mandatory reification when using the 'mod' operator together with 'or'?

I have written a CSP program using CLP(FD) and SWI-Prolog.
I think I need to improve my constraints' writing when I use the mod operator
together with #\/ in my predicates.
A short example :
:- use_module(library(clpfd)).
constr(X,Y,Z) :-
X in {1,2,3,4,5,6,7},
Y in {3,5,7},
Z in {1,2},
((X #= 3)) #==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)),
((Z #= 1)) #<==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)).
If I call constr(3,Y,Z)., I get Z #= 1or Z #= 2.
This is because some intermediate variables (relative to the mod expressions) still need to be evaluated.
Of course the ideal would be to only obtain Z #= 1.
How could this be done ?
I know that if I write instead
((X #= 3)) #==> ((Z #= 1)),
((Z #= 1)) #<==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)).
everything works as expected.
But is this reification mandatory ? I mean, do I have to create a reification variable each time I have this pattern in my constraints :
(A mod n1 #= 0) #\/ (B mod n2 #= 0) #\/ ... #\/ (Z mod n26 #= 0)
Thanks in advance for your ideas.
That's a very good observation and question! First, please note that this is in no way specific to mod/2. For example:
?- B #<==> X #= Y+Z, X #= Y+Z.
B in 0..1,
X#=_G1122#<==>B,
Y+Z#=X,
Y+Z#=_G1122.
In contrast, if we write this declaratively equivalently as:
?- B #<==> X #= A, A #= Y + Z, X #= A.
then we get exactly as expected:
A = X,
B = 1,
Y+Z#=X.
What is going on here? In all systems I am aware of, reification in general uses a decomposition of CLP(FD) expressions which unfortunately removes important information that is not recovered later. In the first example, it is not detected that the constraint X #= Y+Z is entailed, i.e., necessarily holds.
On the other hand, entailment of a single equality with non-composite arguments is correctly detected, as in the second example.
So yes, in general, you will need to rewrite your constraints in this way to enable optimal detection of entailment.
The lurking question is of course whether the CLP(FD) system could help you to detect such cases and perform the rewriting automatically. Also in this case, the answer is yes, at least for certain cases. However, the CLP(FD) system typically is told only individual constraints in a certain sequence, and recreating and analyzing a global overview of all posted constraints in order to merge or combine previously decomposed constraints is typically not worth the effort.
With the (semi-official) contracting/1 predicate, you can minimize some domains in one fell swoop. In your case:
| ?- constr(3,Y,Z).
clpz:(Z#=1#<==>_A),
clpz:(_B#=0#<==>_C),
clpz:(_D#=0#<==>_E),
clpz:(_F#=0#<==>_G),
clpz:(_H#=0#<==>_I),
clpz:(_C#\/_E#<==>1),
clpz:(_G#\/_I#<==>_A),
clpz:(Y mod 3#=_B),
clpz:(Y mod 3#=_F),
clpz:(Y mod 7#=_D),
clpz:(Y mod 7#=_H),
clpz:(Y in 3\/5\/7),
clpz:(Z in 1..2),
clpz:(_C in 0..1),
clpz:(_B in 0..2),
clpz:(_E in 0..1),
clpz:(_D in 0..6),
clpz:(_A in 0..1),
clpz:(_G in 0..1),
clpz:(_F in 0..2),
clpz:(_I in 0..1),
clpz:(_H in 0..6) ? ;
no
And now by adding a single goal:
| ?- constr(3,Y,Z), clpz:contracting([Z]).
Z = 1,
clpz:(_A#=0#<==>_B),
clpz:(_C#=0#<==>_D),
clpz:(_E#=0#<==>_F),
clpz:(_G#=0#<==>_H),
clpz:(_B#\/_D#<==>1),
clpz:(_F#\/_H#<==>1),
clpz:(Y mod 3#=_A),
clpz:(Y mod 3#=_E),
clpz:(Y mod 7#=_C),
clpz:(Y mod 7#=_G),
clpz:(Y in 3\/5\/7),
clpz:(_B in 0..1),
clpz:(_A in 0..2),
clpz:(_D in 0..1),
clpz:(_C in 0..6),
clpz:(_F in 0..1),
clpz:(_E in 0..2),
clpz:(_H in 0..1),
clpz:(_G in 0..6) ? ;
no
In other words, a more consistent version of your predicate constr/3 would be:
constr_better(X, Y, Z) :-
constr(X, Y, Z),
clpz:contracting([Z]).
Above I used SICStus with library(clpz) which is the successor to library(clpfd) of SWI which has clpfd:contracting/1, too.
After trying many things, I ended up with these conclusions, tell me if I am wrong (sorry, I am a beginner).
Let's consider this sample :
:- use_module(library(clpfd)).
constr(X,Y,Z) :-
X in {1,2,3,4,5,6,7},
Y in {3,5,7,21,42},
Z in {1,2},
(X #= 3) #==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)),
(Z #= 1) #<==> ((Y mod 3 #= 0) #\/ (Y mod 7 #= 0)).
constr_better(X,Y,Z) :- constr(X,Y,Z), clpfd:contracting([X,Y,Z]).
res(X,L) :- setof(X, indomain(X), L).
constrChoice(X,Y,Z,XOut,YOut,ZOut) :-
constr(X,Y,Z),
res(X,XOut),res(Y,YOut),res(Z,ZOut).
constrChoiceBetter(X,Y,Z,XOut,YOut,ZOut) :-
constr_better(X,Y,Z),
res(X,XOut),res(Y,YOut),res(Z,ZOut).
constr(3,Y,Z) gives Z in 1..2 but constrChoice(3,Y,Z,Xout,Yout,Zout) gives Zout=[1] , so no need to use contracting/1 because the use of setof/3 together with indomain/1 does the job. No need to rewrite prolog predicates either.
Now if I have AND #/\ instead of OR #\/, none of the calls constr(3,Y,Z),constrChoice(3,Y,Z,Xout,Yout,Zout) or constrChoiceBetter(3,Y,Z,Xout,Yout,Zout) gives that Z must be 1. I effectively have that Y is 21 or 42, but Z is told to be 1 or 2.
What works : write that Y mod 21 #= 0 directly, and then no need to use contracting/1 either.
Thanks for your comments.

Resources