Calculating whether number is prime in Prolog

☆樱花仙子☆ 提交于 2019-12-11 03:38:47

问题


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?


回答1:


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



回答2:


Kay already provided a working modification of the broken program. I'll provide a simple analysis of what's broken.

When solving a problem in Prolog, it's good to be able to write out logically what it is you want first. In this case, it appears that you want to declare that:

A number, A, is prime if, for each number B < A, the value of A mod B is non-zero.

There are probably a couple of ways to render this directly into Prolog, of which Kay shows one.

However, the way your original rules are written, they say:

A number, A, is prime if:
    (Rule 1) The value of A mod B, for a given value of B, is 1 and is also A.
 OR (Rule 2) B < A and Rule 1 is satisfied with A and B+1.

As you can see, the rules as defined have a few issues:

  • The rules don't match the logical definition of prime described in terms of the modulo relationship between the original number and all the numbers less than itself.
  • The first rule expects an impossible mathematical condition when A is not equal to 1 (remember, the comma [,] in Prolog is a conjunction)
  • The rules are initiated with starting divisor of 1, which is probably bad since 1 divides everything and is likely to become an exception to any rules that work

EDIT

Getting back to the first definition of a prime using the modulo operator, we can translate that into Prolog as follows:

is_prime(N) :-                 % N is prime if...
  N > 1,                       % N > 1, and
  non_divisible_from(N, 2).    % N is non-divisible by everything from 2 to N-1

non_divisible_from(N, D) :-    % N is non-divisible by D through N-1 if...
  N =< D.                      % D >= N
                               % --OR--
non_divisible_from(N, D) :-    % N is non-divisible from D to N-1 if...
  N > D,                       % N > D, and
  N mod D =\= 0,               % N is non-divisible by D, and
  D1 is D + 1,                 % N is non-divisible by D+1 to N-1
  non_divisible_from(N, D1).

This logic is basically the same as Kay's except he's using a Prolog if-then-else construct.



来源:https://stackoverflow.com/questions/25972392/calculating-whether-number-is-prime-in-prolog

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!