Skip to content

Commit 7261959

Browse files
committed
Add Miller-Rabin primality test algorithm
1 parent a21abe6 commit 7261959

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.thealgorithms.randomized;
2+
3+
import java.math.BigInteger;
4+
import java.util.Random;
5+
6+
/**
7+
* The Miller–Rabin primality test
8+
*
9+
* Use case:
10+
*
11+
* - Determine whether a number is probably prime or definitely composite.
12+
*
13+
* In cryptography very big prime numbers are required.
14+
* A popular procedure to generate a prime number with n bits is to generate a random number and check its primality.
15+
* This is repeated until a prime number is found.
16+
*
17+
* Time Complexity: O(k log n)
18+
* Space Complexity: O(k)
19+
* where k - number of iterations, i.e. number of random primes to test on.
20+
*
21+
*
22+
* @author DomTr (https://github.com/DomTr)
23+
* @see <a href="https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test"> Miller Rabin primality test - Wikipedia </a>
24+
*
25+
*/
26+
public final class MillerRabinPrimality {
27+
private static final BigInteger ZERO = BigInteger.ZERO;
28+
private static final BigInteger ONE = BigInteger.ONE;
29+
private static final BigInteger TWO = new BigInteger("2");
30+
private static final BigInteger THREE = new BigInteger("3");
31+
private static final BigInteger FOUR = new BigInteger("4");
32+
private static final Random rand = new Random();
33+
private MillerRabinPrimality() {
34+
throw new UnsupportedOperationException("Utility class");
35+
}
36+
/**
37+
* Performs the Miller-Rabin probabilistic primality test on the given number.
38+
*
39+
* @param n the number to test for primality
40+
* @return true if n is probably prime, false if it is composite.
41+
* The test never falsely classifies a prime as composite (no false negatives),
42+
* but it can mistakenly identify a composite as probably prime (false positives),
43+
* although this probability is very low and decreases exponentially with the number of bases tested.
44+
*/
45+
public static boolean millerRabin(BigInteger n, int iter) {
46+
if (n.compareTo(ONE) <= 0 || n.equals(FOUR)) return false;
47+
if (n.equals(THREE)||n.equals(TWO)) return true;
48+
long deg = 0;
49+
BigInteger oddPart = n.subtract(ONE);
50+
while(oddPart.mod(TWO).equals(ZERO)) {
51+
oddPart = oddPart.divide(TWO);
52+
deg++;
53+
}
54+
55+
while(iter-- > 0) {
56+
if (checkComposite(n, oddPart, deg)) {
57+
return false;
58+
}
59+
}
60+
return true;
61+
}
62+
63+
/**
64+
* Checks whether the given base 'a' is a witness to the compositeness of 'n'
65+
* in the Miller-Rabin primality test.
66+
*
67+
* @param n the number being tested for primality
68+
* @param oddPart the odd part of n-1 (i.e., n - 1 = 2^deg * oddPart)
69+
* @param deg the exponent of 2 in the factorization of n-1
70+
* @return true if 'a' is a witness that 'n' is composite;
71+
* e false if 'n' might still be prime with respect to this base
72+
*/
73+
public static boolean checkComposite(BigInteger n, BigInteger oddPart, long deg) {
74+
BigInteger a = getRandom(TWO, n.subtract(TWO));
75+
BigInteger x = a.modPow(oddPart, n);
76+
if (x.equals(n.subtract(ONE)) || x.equals(ONE)) {
77+
return false;
78+
}
79+
long tmpDeg = 1;
80+
while (tmpDeg < deg) {
81+
x = x.modPow(BigInteger.valueOf(2), n);
82+
tmpDeg++;
83+
if (x.equals(n.subtract(ONE))) {
84+
return false;
85+
}
86+
}
87+
return true;
88+
}
89+
/*
90+
* Returns a random BigInteger in [from, to) interval
91+
*
92+
* @param from - lowest value
93+
* @param to - highest value
94+
*/
95+
private static BigInteger getRandom(BigInteger from, BigInteger to) {
96+
BigInteger res;
97+
do {
98+
res = new BigInteger(from.bitLength(), rand);
99+
} while (res.compareTo(from) < 0 || res.compareTo(to) >= 0);
100+
101+
return res;
102+
}
103+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.thealgorithms.randomized;
2+
import static org.junit.jupiter.api.Assertions.assertTrue;
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
5+
import java.math.BigInteger;
6+
import org.junit.jupiter.api.Test;
7+
8+
/*
9+
* Tests for MillerRabinPrimality
10+
* @author DomTr (https://github.com/DomTr)
11+
*/
12+
13+
public class MillerRabinPrimalityTest {
14+
static final int iter = 10;
15+
16+
@Test
17+
public void testComposites() {
18+
long[] values = {1, 25, 2932021007403L, 4501680375506332L, 6910906992394051L,
19+
4887521073398877L, 5577943644815725L, 6085993686552764L};
20+
21+
for (long v : values) {
22+
BigInteger val = BigInteger.valueOf(v);
23+
assertFalse(MillerRabinPrimality.millerRabin(val, iter));
24+
}
25+
}
26+
@Test
27+
public void testPrimes() {
28+
long[] values = {2, 17, 137, 317, 405857, 2932031007403L, 6333369275038567L};
29+
for (long v : values) {
30+
BigInteger val = BigInteger.valueOf(v);
31+
assertTrue(MillerRabinPrimality.millerRabin(val, iter));
32+
}
33+
}
34+
35+
// Test all primes
36+
@Test
37+
public void testBigPrimes() {
38+
BigInteger b1 = new BigInteger("423726770669791241889982933129");
39+
BigInteger b2 = new BigInteger("728801495170617624430641064729");
40+
BigInteger b3 = new BigInteger("715069831641887124233793734953");
41+
BigInteger b4 = new BigInteger("214668004859466264786404914307");
42+
BigInteger b5 = new BigInteger("107280976690907382021651905569");
43+
BigInteger b6 = new BigInteger("194139053422804244228680212551");
44+
BigInteger b7 = new BigInteger("225220037755960690862092087151");
45+
assertTrue(MillerRabinPrimality.millerRabin(b1, iter));
46+
assertTrue(MillerRabinPrimality.millerRabin(b2, iter));
47+
assertTrue(MillerRabinPrimality.millerRabin(b3, iter));
48+
assertTrue(MillerRabinPrimality.millerRabin(b4, iter));
49+
assertTrue(MillerRabinPrimality.millerRabin(b5, iter));
50+
assertTrue(MillerRabinPrimality.millerRabin(b6, iter));
51+
assertTrue(MillerRabinPrimality.millerRabin(b7, iter));
52+
}
53+
// Tests composite numbers which are products of two big primes
54+
@Test
55+
public void testBigComposites() {
56+
BigInteger p1 = new BigInteger("995224294347733"); // all 4 primes
57+
BigInteger p2 = new BigInteger("990601545052177");
58+
BigInteger p3 = new BigInteger("924286031819653");
59+
BigInteger p4 = new BigInteger("408464000499539");
60+
61+
62+
assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p1), iter));
63+
assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p2), iter));
64+
assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p3), iter));
65+
assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p4), iter));
66+
67+
assertFalse(MillerRabinPrimality.millerRabin(p2.multiply(p2), iter));
68+
assertFalse(MillerRabinPrimality.millerRabin(p2.multiply(p3), iter));
69+
assertFalse(MillerRabinPrimality.millerRabin(p2.multiply(p4), iter));
70+
71+
assertFalse(MillerRabinPrimality.millerRabin(p3.multiply(p3), iter));
72+
assertFalse(MillerRabinPrimality.millerRabin(p3.multiply(p4), iter));
73+
74+
assertFalse(MillerRabinPrimality.millerRabin(p4.multiply(p4), iter));
75+
}
76+
}

0 commit comments

Comments
 (0)