Reputation: 251
I want to use nextProbablePrime()
method of BigInteger
to get the prime that is lower than the given number instead of higher.
Is it possible to get it using just one nextProbablePrime
call?
Upvotes: 4
Views: 831
Reputation: 2321
I don't know whether it's possible using the nextProbablePrime
method (in one call). However, I just had a need for a previousProbablePrime
method, and I came up with the following method that uses the isProbablePrime
method in the BigInteger
API:
public static BigInteger previousProbablePrime(BigInteger val) {
// To achieve the same degree of certainty as the nextProbablePrime
// method, use x = 100 --> 2^(-100) == (0.5)^100.
int certainty = 100;
do {
val = val.subtract(BigInteger.ONE);
} while (!val.isProbablePrime(certainty));
return val;
}
I set up the following test just to compare the speed (and accuracy) to the nextProbablePrime
method:
private static void testPreviousProbablePrime() {
BigInteger min = BigInteger.ONE; // exclusive
BigInteger max = BigInteger.valueOf(1000000); // exclusive
BigInteger val;
// Create a list of prime numbers in the range given by min and max
// using previousProbablePrime method.
ArrayList<BigInteger> listPrev = new ArrayList<BigInteger>();
Stopwatch sw = new Stopwatch();
sw.start();
val = BigIntegerUtils.previousProbablePrime(max);
while (val.compareTo(min) > 0) {
listPrev.add(val);
val = BigIntegerUtils.previousProbablePrime(val);
}
sw.stop();
System.out.println("listPrev = " + listPrev.toString());
System.out.println("number of items in list = " + listPrev.size());
System.out.println("previousProbablePrime time = " + sw.getHrMinSecMsElapsed());
System.out.println();
// Create a list of prime numbers in the range given by min and max
// using nextProbablePrime method.
ArrayList<BigInteger> listNext = new ArrayList<BigInteger>();
sw.reset();
sw.start();
val = min.nextProbablePrime();
while (val.compareTo(max) < 0) {
listNext.add(val);
val = val.nextProbablePrime();
}
sw.stop();
System.out.println("listNext = " + listNext.toString());
System.out.println("number of items in list = " + listNext.size());
System.out.println("nextProbablePrime time = " + sw.getHrMinSecMsElapsed());
System.out.println();
// Compare the two lists.
boolean identical = true;
int lastIndex = listPrev.size() - 1;
for (int i = 0; i <= lastIndex; i++) {
int j = lastIndex - i;
if (listPrev.get(j).compareTo(listNext.get(i)) != 0) {
identical = false;
break;
}
}
System.out.println("Lists are identical? " + identical);
}
The Stopwatch
class is just a basic custom class to track the execution time, so modify those parts to suit the class you might have for this.
I tested for the ranges from 1 to 10000, 100000, and 1000000. The previousProbablePrime
method took longer to execute in all three tests. However, it appeared that the difference in execution time increased only modestly with each 10x increase in range size. For 10000, previousProbablePrime
executed in just under a second, while nextProbablePrime
came in at around 200 ms, for a difference of about 700 or 800 ms. For 1000000, the difference was only about 2 seconds, even though the execution times were 9 and 7 seconds, respectively. Conclusion, the difference in execution time increases slower than the range size.
In all tests, the two lists contained the identical set of prime numbers.
This level of efficiency was sufficient for my needs...might work for you too.
EDIT
Modified method implementation to be more efficient and potentially faster, though I have not tested.
public static BigInteger previousProbablePrime(BigInteger val) {
if (val.compareTo(BigInteger.TWO) < 0) {
throw new IllegalArgumentException("Value must be greater than 1.");
}
// Handle single unique case where even prime is returned.
if (val.equals(new BigInteger("3"))) {
return BigInteger.TWO;
}
// To achieve the same degree of certainty as the nextProbablePrime
// method, use x = 100 --> 2^(-100) == (0.5)^100.
int certainty = 100;
boolean isEven = val.mod(BigInteger.TWO).equals(BigInteger.ZERO);
val = isEven ? val.subtract(BigInteger.ONE) : val.subtract(BigInteger.TWO);
while (!val.isProbablePrime(certainty)) {
// At this point, only check odd numbers.
val = val.subtract(BigInteger.TWO);
}
return val;
}
Upvotes: 2