Skip to content

Add miller rabin primality #6293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/main/java/com/thealgorithms/randomized/MillerRabinPrimality.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.thealgorithms.randomized;

import java.math.BigInteger;
import java.util.Random;

/**
* The Miller–Rabin primality test
*
* Use case:
*
* - Determine whether a number is probably prime or definitely composite.
*
* In cryptography very big prime numbers are required.
* A popular procedure to generate a prime number with n bits is to generate a random number and check its primality.
* This is repeated until a prime number is found.
*
* Time Complexity: O(k log n)
* Space Complexity: O(k)
* where k - number of iterations, i.e. number of random primes to test on.
*
*
* @author DomTr (https://github.com/DomTr)
* @see <a href="https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test"> Miller Rabin primality test - Wikipedia </a>
*
*/
public final class MillerRabinPrimality {
private static final BigInteger ZERO = BigInteger.ZERO;
private static final BigInteger ONE = BigInteger.ONE;
private static final BigInteger TWO = new BigInteger("2");
private static final BigInteger THREE = new BigInteger("3");
private static final BigInteger FOUR = new BigInteger("4");
private static final Random rand = new Random();
private MillerRabinPrimality() {
throw new UnsupportedOperationException("Utility class");
}
/**
* Performs the Miller-Rabin probabilistic primality test on the given number.
*
* @param n the number to test for primality
* @return true if n is probably prime, false if it is composite.
* The test never falsely classifies a prime as composite (no false negatives),
* but it can mistakenly identify a composite as probably prime (false positives),
* although this probability is very low and decreases exponentially with the number of bases tested.
*/
public static boolean millerRabin(BigInteger n, int iter) {
if (n.compareTo(ONE) <= 0 || n.equals(FOUR)) return false;
if (n.equals(THREE) || n.equals(TWO)) return true;
long deg = 0;
BigInteger oddPart = n.subtract(ONE);
while (oddPart.mod(TWO).equals(ZERO)) {
oddPart = oddPart.divide(TWO);
deg++;
}

while (iter-- > 0) {
if (checkComposite(n, oddPart, deg)) {
return false;
}
}
return true;
}

/**
* Checks whether the given base 'a' is a witness to the compositeness of 'n'
* in the Miller-Rabin primality test.
*
* @param n the number being tested for primality
* @param oddPart the odd part of n-1 (i.e., n - 1 = 2^deg * oddPart)
* @param deg the exponent of 2 in the factorization of n-1
* @return true if 'a' is a witness that 'n' is composite;
* e false if 'n' might still be prime with respect to this base
*/
public static boolean checkComposite(BigInteger n, BigInteger oddPart, long deg) {
BigInteger a = getRandom(TWO, n.subtract(TWO));
BigInteger x = a.modPow(oddPart, n);
if (x.equals(n.subtract(ONE)) || x.equals(ONE)) {
return false;
}
long tmpDeg = 1;
while (tmpDeg < deg) {
x = x.modPow(BigInteger.valueOf(2), n);
tmpDeg++;
if (x.equals(n.subtract(ONE))) {
return false;
}
}
return true;
}
/*
* Returns a random BigInteger in [from, to) interval
*
* @param from - lowest value
* @param to - highest value
*/
private static BigInteger getRandom(BigInteger from, BigInteger to) {
BigInteger res;
do {
res = new BigInteger(from.bitLength(), rand);
} while (res.compareTo(from) < 0 || res.compareTo(to) >= 0);

return res;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.thealgorithms.randomized;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigInteger;
import org.junit.jupiter.api.Test;

/*
* Tests for MillerRabinPrimality
* @author DomTr (https://github.com/DomTr)
*/

public class MillerRabinPrimalityTest {
static final int iter = 10;

@Test
public void testComposites() {
long[] values = {1, 25, 2932021007403L, 4501680375506332L, 6910906992394051L, 4887521073398877L, 5577943644815725L, 6085993686552764L};

for (long v : values) {
BigInteger val = BigInteger.valueOf(v);
assertFalse(MillerRabinPrimality.millerRabin(val, iter));
}
}
@Test
public void testPrimes() {
long[] values = {2, 17, 137, 317, 405857, 2932031007403L, 6333369275038567L};
for (long v : values) {
BigInteger val = BigInteger.valueOf(v);
assertTrue(MillerRabinPrimality.millerRabin(val, iter));
}
}

// Test all primes
@Test
public void testBigPrimes() {
BigInteger b1 = new BigInteger("423726770669791241889982933129");
BigInteger b2 = new BigInteger("728801495170617624430641064729");
BigInteger b3 = new BigInteger("715069831641887124233793734953");
BigInteger b4 = new BigInteger("214668004859466264786404914307");
BigInteger b5 = new BigInteger("107280976690907382021651905569");
BigInteger b6 = new BigInteger("194139053422804244228680212551");
BigInteger b7 = new BigInteger("225220037755960690862092087151");
assertTrue(MillerRabinPrimality.millerRabin(b1, iter));
assertTrue(MillerRabinPrimality.millerRabin(b2, iter));
assertTrue(MillerRabinPrimality.millerRabin(b3, iter));
assertTrue(MillerRabinPrimality.millerRabin(b4, iter));
assertTrue(MillerRabinPrimality.millerRabin(b5, iter));
assertTrue(MillerRabinPrimality.millerRabin(b6, iter));
assertTrue(MillerRabinPrimality.millerRabin(b7, iter));
}
// Tests composite numbers which are products of two big primes
@Test
public void testBigComposites() {
BigInteger p1 = new BigInteger("995224294347733"); // all 4 primes
BigInteger p2 = new BigInteger("990601545052177");
BigInteger p3 = new BigInteger("924286031819653");
BigInteger p4 = new BigInteger("408464000499539");

assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p1), iter));
assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p2), iter));
assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p3), iter));
assertFalse(MillerRabinPrimality.millerRabin(p1.multiply(p4), iter));

assertFalse(MillerRabinPrimality.millerRabin(p2.multiply(p2), iter));
assertFalse(MillerRabinPrimality.millerRabin(p2.multiply(p3), iter));
assertFalse(MillerRabinPrimality.millerRabin(p2.multiply(p4), iter));

assertFalse(MillerRabinPrimality.millerRabin(p3.multiply(p3), iter));
assertFalse(MillerRabinPrimality.millerRabin(p3.multiply(p4), iter));

assertFalse(MillerRabinPrimality.millerRabin(p4.multiply(p4), iter));
}
}
Loading