Miller–Rabin primality test
You are encouraged to solve this task according to the task description, using any language you may know.
This page uses content from Wikipedia. The original article was at Miller–Rabin primality test. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance) |
The Miller–Rabin primality test or Rabin–Miller primality test is a primality test: an algorithm which determines whether a given number is prime or not. The algorithm, as modified by Michael O. Rabin to avoid the generalized Riemann hypothesis, is a probabilistic algorithm.
The pseudocode, from Wikipedia is:
Input: n > 2, an odd integer to be tested for primality; k, a parameter that determines the accuracy of the test Output: composite if n is composite, otherwise probably prime write n − 1 as 2s·d with d odd by factoring powers of 2 from n − 1 LOOP: repeat k times: pick a randomly in the range [2, n − 2] x ← ad mod n if x = 1 or x = n − 1 then do next LOOP for r = 1 .. s − 1 x ← x2 mod n if x = 1 then return composite if x = n − 1 then do next LOOP return composite return probably prime
- The nature of the test involves big numbers, so the use of "big numbers" libraries (or similar features of the language of your choice) are suggested, but not mandatory.
- Deterministic variants of the test exist and can be implemented as extra (not mandatory to complete the task)
Bc
<lang bc>next = 1; # seed of the random generator scale = 0;
- random generator (according to POSIX...); since
- this gives number between 0 and 32767, we can
- test only numbers in this range.
define rand() {
next = next * 1103515245 + 12345; return ((next/65536) % 32768);
}
define rangerand(from, to) {
return (from + rand()%(to-from+1));
}
define miller_rabin_test(n, k)
{
auto d, r, a, x, s;
if ( n <= 3) { return(1); } if ( (n % 2) == 0) { return(0); }
# find s and d so that d*2^s = n-1 d = n-1; s = 0; while( (d%2) == 0 ) { d = d/2; s += 1; }
while(k-- > 0) { a = rangerand(2, n-2); x = (a^d) % n; if ( x != 1 ) { for(r=0; r < s; r++) { if ( x == (n-1) ) { break; } x = (x*x) % n; } if ( x != (n-1) ) { return (0); } } } return (1);
}
for(i=1; i < 1000; i++) {
if ( miller_rabin_test(i, 10) == 1 ) { i }
} quit;</lang>
C
miller-rabin.h
<lang c>#ifndef _MILLER_RABIN_H_
- define _MILLER_RABIN_H
- include <gmp.h>
bool miller_rabin_test(mpz_t n, int j);
- endif</lang>
miller-rabin.c
For decompose
(and header primedecompose.h), see Prime decomposition.
<lang c>#include <stdbool.h>
- include <gmp.h>
- include "primedecompose.h"
- define MAX_DECOMPOSE 100
bool miller_rabin_test(mpz_t n, int j) {
bool res; mpz_t f[MAX_DECOMPOSE]; mpz_t s, d, a, x, r; mpz_t n_1, n_3; gmp_randstate_t rs; int l=0, k;
res = false; gmp_randinit_default(rs);
mpz_init(s); mpz_init(d); mpz_init(a); mpz_init(x); mpz_init(r); mpz_init(n_1); mpz_init(n_3);
if ( mpz_cmp_si(n, 3) <= 0 ) { // let us consider 1, 2, 3 as prime gmp_randclear(rs); return true; } if ( mpz_odd_p(n) != 0 ) { mpz_sub_ui(n_1, n, 1); // n-1 mpz_sub_ui(n_3, n, 3); // n-3 l = decompose(n_1, f); mpz_set_ui(s, 0); mpz_set_ui(d, 1); for(k=0; k < l; k++) { if ( mpz_cmp_ui(f[k], 2) == 0 )
mpz_add_ui(s, s, 1);
else
mpz_mul(d, d, f[k]);
} // 2^s * d = n-1 while(j-- > 0) { mpz_urandomm(a, rs, n_3); // random from 0 to n-4 mpz_add_ui(a, a, 2); // random from 2 to n-2 mpz_powm(x, a, d, n); if ( mpz_cmp_ui(x, 1) == 0 ) continue; mpz_set_ui(r, 0); while( mpz_cmp(r, s) < 0 ) {
if ( mpz_cmp(x, n_1) == 0 ) break; mpz_powm_ui(x, x, 2, n); mpz_add_ui(r, r, 1);
} if ( mpz_cmp(x, n_1) == 0 ) continue; goto flush; // woops } res = true; }
flush:
for(k=0; k < l; k++) mpz_clear(f[k]); mpz_clear(s); mpz_clear(d); mpz_clear(a); mpz_clear(x); mpz_clear(r); mpz_clear(n_1); mpz_clear(n_3); gmp_randclear(rs); return res;
}</lang>
Testing
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <stdbool.h>
- include <gmp.h>
- include "miller-rabin.h"
- define PREC 10
- define TOP 4000
int main() {
mpz_t num;
mpz_init(num); mpz_set_ui(num, 1); while ( mpz_cmp_ui(num, TOP) < 0 ) { if ( miller_rabin_test(num, PREC) ) { gmp_printf("%Zd maybe prime\n", num); } /*else { gmp_printf("%Zd not prime\n", num); }*/ // remove the comment iff you're interested in // sure non-prime. mpz_add_ui(num, num, 1); }
mpz_clear(num); return EXIT_SUCCESS;
}</lang>
Fortran
For the module PrimeDecompose, see Prime decomposition.
<lang fortran>module Miller_Rabin
use PrimeDecompose implicit none
integer, parameter :: max_decompose = 100
private :: int_rrand, max_decompose
contains
function int_rrand(from, to) integer(huge) :: int_rrand integer(huge), intent(in) :: from, to
real :: o call random_number(o) int_rrand = floor(from + o * real(max(from,to) - min(from, to))) end function int_rrand
function miller_rabin_test(n, k) result(res) logical :: res integer(huge), intent(in) :: n integer, intent(in) :: k integer(huge), dimension(max_decompose) :: f integer(huge) :: s, d, i, a, x, r
res = .true. f = 0
if ( (n <= 2) .and. (n > 0) ) return if ( mod(n, 2) == 0 ) then res = .false. return end if
call find_factors(n-1, f) s = count(f == 2) d = (n-1) / (2 ** s) loop: do i = 1, k a = int_rrand(2_huge, n-2) x = mod(a ** d, n) if ( x == 1 ) cycle do r = 0, s-1 if ( x == ( n - 1 ) ) cycle loop x = mod(x*x, n) end do if ( x == (n-1) ) cycle res = .false. return end do loop res = .true. end function miller_rabin_test
end module Miller_Rabin</lang>
Testing
<lang fortran>program TestMiller
use Miller_Rabin implicit none
integer, parameter :: prec = 30 integer(huge) :: i
! this is limited since we're not using a bignum lib call do_test( (/ (i, i=1, 29) /) )
contains
subroutine do_test(a) integer(huge), dimension(:), intent(in) :: a
integer :: i do i = 1, size(a,1) print *, a(i), miller_rabin_test(a(i), prec) end do
end subroutine do_test
end program TestMiller</lang>
Possible improvements: create bindings to the GMP library, change integer(huge)
into something like type(huge_integer)
, write a lots of interfaces to allow to use big nums naturally (so that the code will be unchanged, except for the changes said above)