Search code examples
prologprimesprimality-test

Calculating whether number is prime in Prolog


I am trying to calculate if the input is a prime number but something goes wrong... here's my code:

primeNumber(X):-
    prime_prime(A, 1).

prime_prime(A, B):-
    R is A mod B,
    R =:= 1,
    R =:= A.
prime_prime(X, B):-
    B < A,
    Next is B + 1,
    prime_prime(A, Next).

It gives me false every time. Anyone got any clues or idea on what I am doing wrong?


Solution

  • See http://www.swi-prolog.org/pldoc/man?function=mod/2:

    +IntExpr1 mod +IntExpr2
    Modulo, defined as Result = IntExpr1 - (IntExpr1 div IntExpr2) × IntExpr2, where div is floored division.

    So R should be 0. mod has only one result.

    A working solution would be:

    primeNumber(A) :-
        A > 1,                 % Negative numbers, 0 and 1 are not prime.
        prime_prime(A, 2).     % Begin iteration:
    
    prime_prime(A, B) :-       % Test if A divides by B without remainder
        B >= A                 % The limit was reached?
        ->  true               %     Then it's prime.
        ;   0 is A mod B       % B divides A without a remainder?
        ->  false              %     Then it's not prime.
        ;   succ(B, C),        % Otherwise: C is B + 1
            prime_prime(A, C). % Test if C divides A.
    

    By the way, primeNumber/1 (a predicate named primeNumber, with one argument) is a totally separate predicate from primeNumber/2 (same name, two arguments). A "subfunction" that only gets an extra argument for the start value, is usually given the same name. So instead of prime_prime you should just use primeNumber, though in Prolog you normally don't use camelCase.

    Using the optimization that Sergei Lodyagin proposed in the comments:

    primeNumber(A) :-
        A > 1,                    % Negative numbers, 0 and 1 are not prime.
        sqrt(A, L),               % A prime factor of A is =< the square root of A.
        prime_prime(A, 2, L).     % Begin iteration:
    
    prime_prime(A, B, L) :-       % Test if A divides by B without remainder
        B >= L                    % The limit was reached?
        ->  true                  %     Then it's prime.
        ;   0 is A mod B          % B divides A without a remainder?
        ->  false                 %     Then it's not prime.
        ;   succ(B, C),           % Otherwise: C is B + 1
            prime_prime(A, C, L). % Test if C divides A.
    

    And if you use the predefined predicate between(+Low, +High, ?Value):

    primeNumber(A) :-
        L is floor(sqrt(A)),
        \+ (between(2, L, X),
            0 is A mod X).
    

    And to reduce the number of iterations even further, you only need to test for odd modules:

    primeNumber(2).
    primeNumber(A) :-
        A > 2,
        \+ 0 is A mod 2,
        L is floor(sqrt(A) / 2),
        \+ (between(1, L, X),
            0 is A mod (1 + 2*X)).