Miller-Rabin Prime test (Speed is the main goal)
The code below is for testing Primes.
testPrime(100000);
testPrime(1000000);
testPrime(10000000);
testPrime(100000000);
Now my goal is to make the code super fast at finding prime number, min-max, close prime.
Are there any improvements that can be done to the code below to improve the speed for IsPrime
?
import java.util.ArrayList;
import java.math.*;
class Primes {
private static long as = {2, 7, 61};
private static long modpow(long x, long c, long m) {
long result = 1;
long aktpot = x;
while (c > 0) {
if (c % 2 == 1) {
result = (result * aktpot) % m;
}
aktpot = (aktpot * aktpot) % m;
c /= 2;
}
return result;
}
private static boolean millerRabin(long n) {
outer:
for (long a : as) {
if (a < n) {
long s = 0;
long d = n - 1;
while (d % 2 == 0) {
s++;
d /= 2;
}
long x = modpow(a, d, n);
if (x != 1 && x != n - 1) {
for (long r = 1; r < s; r++) {
x = (x * x) % n;
if (x == 1) {
return false;
}
if (x == n - 1) {
continue outer;
}
}
return false;
}
}
}
return true;
}
public static boolean IsPrime(long num) {
if (num <= 1) {
return false;
} else if (num <= 3) {
return true;
} else if (num % 2 == 0) {
return false;
} else {
return millerRabin(num);
}
}
public static int primes(int min, int max) {
ArrayList<Integer> primesList = new ArrayList<Integer>();
for( int i=min; i<max; i++ ){
if( IsPrime(i) ){
primesList.add(i);
}
}
int primesArray = new int[primesList.size()];
for(int i=0; i<primesArray.length; i++){
primesArray[i] = (int) primesList.get(i);
}
return primesArray;
}
public static String tostring (int arr){
String ans="";
for (int i=0; i<arr.length;i++){
ans= ans+arr[i]+ " ";
}
return ans;
}
public static int closestPrime(int num) {
int count=1;
for (int i=num;;i++){
int plus=num+count, minus=num-count;
if (IsPrime(minus)){
return minus;
}
if (IsPrime(plus)) {
return plus;
}
count=count+1;
}
}
}
//end class
java performance primes
|
show 1 more comment
The code below is for testing Primes.
testPrime(100000);
testPrime(1000000);
testPrime(10000000);
testPrime(100000000);
Now my goal is to make the code super fast at finding prime number, min-max, close prime.
Are there any improvements that can be done to the code below to improve the speed for IsPrime
?
import java.util.ArrayList;
import java.math.*;
class Primes {
private static long as = {2, 7, 61};
private static long modpow(long x, long c, long m) {
long result = 1;
long aktpot = x;
while (c > 0) {
if (c % 2 == 1) {
result = (result * aktpot) % m;
}
aktpot = (aktpot * aktpot) % m;
c /= 2;
}
return result;
}
private static boolean millerRabin(long n) {
outer:
for (long a : as) {
if (a < n) {
long s = 0;
long d = n - 1;
while (d % 2 == 0) {
s++;
d /= 2;
}
long x = modpow(a, d, n);
if (x != 1 && x != n - 1) {
for (long r = 1; r < s; r++) {
x = (x * x) % n;
if (x == 1) {
return false;
}
if (x == n - 1) {
continue outer;
}
}
return false;
}
}
}
return true;
}
public static boolean IsPrime(long num) {
if (num <= 1) {
return false;
} else if (num <= 3) {
return true;
} else if (num % 2 == 0) {
return false;
} else {
return millerRabin(num);
}
}
public static int primes(int min, int max) {
ArrayList<Integer> primesList = new ArrayList<Integer>();
for( int i=min; i<max; i++ ){
if( IsPrime(i) ){
primesList.add(i);
}
}
int primesArray = new int[primesList.size()];
for(int i=0; i<primesArray.length; i++){
primesArray[i] = (int) primesList.get(i);
}
return primesArray;
}
public static String tostring (int arr){
String ans="";
for (int i=0; i<arr.length;i++){
ans= ans+arr[i]+ " ";
}
return ans;
}
public static int closestPrime(int num) {
int count=1;
for (int i=num;;i++){
int plus=num+count, minus=num-count;
if (IsPrime(minus)){
return minus;
}
if (IsPrime(plus)) {
return plus;
}
count=count+1;
}
}
}
//end class
java performance primes
Profile it and look where you spent most of the time. I would expect the modpow function., which is not that easy to further optimize.
– tb-
Apr 2 '13 at 13:16
perhapsif (a < n) { continue;}
is better no jump to end of if statement before going to loop, but it would depends on compiler clerverness
– cl-r
Sep 24 '13 at 6:28
Isc % 2 == 1
in Java guaranteed to optimize down to the same asc & 1 == 0
? The reason I ask is because the type ofc
islong
, which is signed, so this might force the JVM to use an actual divide-by-2-and-get-remainder operation rather than simply a boolean 'and' operation to extract the least-significant bit. If so, the speed difference is likely to be very minimal, but it still might be worth profiling things to check. Also,c /= 2
could be written asc >>>= 1
.
– Todd Lehman
Jul 29 '14 at 3:42
By the way, since this is a code review question, I would suggest a comment pointing the reader to en.wikipedia.org/wiki/Miller_rabin or some such page explaining where the list{ 2, 7, 61 }
comes from, and what the limitations are on your input.
– Todd Lehman
Jul 29 '14 at 3:56
1
@ToddLehman I'm sure Java optimizes the division by 2 even for signed operands, but it's slower. Together with the zero test it could be optimized without the adjustment, but I don't know i it happens.
– maaartinus
May 14 '15 at 0:27
|
show 1 more comment
The code below is for testing Primes.
testPrime(100000);
testPrime(1000000);
testPrime(10000000);
testPrime(100000000);
Now my goal is to make the code super fast at finding prime number, min-max, close prime.
Are there any improvements that can be done to the code below to improve the speed for IsPrime
?
import java.util.ArrayList;
import java.math.*;
class Primes {
private static long as = {2, 7, 61};
private static long modpow(long x, long c, long m) {
long result = 1;
long aktpot = x;
while (c > 0) {
if (c % 2 == 1) {
result = (result * aktpot) % m;
}
aktpot = (aktpot * aktpot) % m;
c /= 2;
}
return result;
}
private static boolean millerRabin(long n) {
outer:
for (long a : as) {
if (a < n) {
long s = 0;
long d = n - 1;
while (d % 2 == 0) {
s++;
d /= 2;
}
long x = modpow(a, d, n);
if (x != 1 && x != n - 1) {
for (long r = 1; r < s; r++) {
x = (x * x) % n;
if (x == 1) {
return false;
}
if (x == n - 1) {
continue outer;
}
}
return false;
}
}
}
return true;
}
public static boolean IsPrime(long num) {
if (num <= 1) {
return false;
} else if (num <= 3) {
return true;
} else if (num % 2 == 0) {
return false;
} else {
return millerRabin(num);
}
}
public static int primes(int min, int max) {
ArrayList<Integer> primesList = new ArrayList<Integer>();
for( int i=min; i<max; i++ ){
if( IsPrime(i) ){
primesList.add(i);
}
}
int primesArray = new int[primesList.size()];
for(int i=0; i<primesArray.length; i++){
primesArray[i] = (int) primesList.get(i);
}
return primesArray;
}
public static String tostring (int arr){
String ans="";
for (int i=0; i<arr.length;i++){
ans= ans+arr[i]+ " ";
}
return ans;
}
public static int closestPrime(int num) {
int count=1;
for (int i=num;;i++){
int plus=num+count, minus=num-count;
if (IsPrime(minus)){
return minus;
}
if (IsPrime(plus)) {
return plus;
}
count=count+1;
}
}
}
//end class
java performance primes
The code below is for testing Primes.
testPrime(100000);
testPrime(1000000);
testPrime(10000000);
testPrime(100000000);
Now my goal is to make the code super fast at finding prime number, min-max, close prime.
Are there any improvements that can be done to the code below to improve the speed for IsPrime
?
import java.util.ArrayList;
import java.math.*;
class Primes {
private static long as = {2, 7, 61};
private static long modpow(long x, long c, long m) {
long result = 1;
long aktpot = x;
while (c > 0) {
if (c % 2 == 1) {
result = (result * aktpot) % m;
}
aktpot = (aktpot * aktpot) % m;
c /= 2;
}
return result;
}
private static boolean millerRabin(long n) {
outer:
for (long a : as) {
if (a < n) {
long s = 0;
long d = n - 1;
while (d % 2 == 0) {
s++;
d /= 2;
}
long x = modpow(a, d, n);
if (x != 1 && x != n - 1) {
for (long r = 1; r < s; r++) {
x = (x * x) % n;
if (x == 1) {
return false;
}
if (x == n - 1) {
continue outer;
}
}
return false;
}
}
}
return true;
}
public static boolean IsPrime(long num) {
if (num <= 1) {
return false;
} else if (num <= 3) {
return true;
} else if (num % 2 == 0) {
return false;
} else {
return millerRabin(num);
}
}
public static int primes(int min, int max) {
ArrayList<Integer> primesList = new ArrayList<Integer>();
for( int i=min; i<max; i++ ){
if( IsPrime(i) ){
primesList.add(i);
}
}
int primesArray = new int[primesList.size()];
for(int i=0; i<primesArray.length; i++){
primesArray[i] = (int) primesList.get(i);
}
return primesArray;
}
public static String tostring (int arr){
String ans="";
for (int i=0; i<arr.length;i++){
ans= ans+arr[i]+ " ";
}
return ans;
}
public static int closestPrime(int num) {
int count=1;
for (int i=num;;i++){
int plus=num+count, minus=num-count;
if (IsPrime(minus)){
return minus;
}
if (IsPrime(plus)) {
return plus;
}
count=count+1;
}
}
}
//end class
java performance primes
java performance primes
edited Apr 2 '13 at 13:44
Adam
4,95112344
4,95112344
asked Apr 2 '13 at 9:15
AlaevAlaev
254
254
Profile it and look where you spent most of the time. I would expect the modpow function., which is not that easy to further optimize.
– tb-
Apr 2 '13 at 13:16
perhapsif (a < n) { continue;}
is better no jump to end of if statement before going to loop, but it would depends on compiler clerverness
– cl-r
Sep 24 '13 at 6:28
Isc % 2 == 1
in Java guaranteed to optimize down to the same asc & 1 == 0
? The reason I ask is because the type ofc
islong
, which is signed, so this might force the JVM to use an actual divide-by-2-and-get-remainder operation rather than simply a boolean 'and' operation to extract the least-significant bit. If so, the speed difference is likely to be very minimal, but it still might be worth profiling things to check. Also,c /= 2
could be written asc >>>= 1
.
– Todd Lehman
Jul 29 '14 at 3:42
By the way, since this is a code review question, I would suggest a comment pointing the reader to en.wikipedia.org/wiki/Miller_rabin or some such page explaining where the list{ 2, 7, 61 }
comes from, and what the limitations are on your input.
– Todd Lehman
Jul 29 '14 at 3:56
1
@ToddLehman I'm sure Java optimizes the division by 2 even for signed operands, but it's slower. Together with the zero test it could be optimized without the adjustment, but I don't know i it happens.
– maaartinus
May 14 '15 at 0:27
|
show 1 more comment
Profile it and look where you spent most of the time. I would expect the modpow function., which is not that easy to further optimize.
– tb-
Apr 2 '13 at 13:16
perhapsif (a < n) { continue;}
is better no jump to end of if statement before going to loop, but it would depends on compiler clerverness
– cl-r
Sep 24 '13 at 6:28
Isc % 2 == 1
in Java guaranteed to optimize down to the same asc & 1 == 0
? The reason I ask is because the type ofc
islong
, which is signed, so this might force the JVM to use an actual divide-by-2-and-get-remainder operation rather than simply a boolean 'and' operation to extract the least-significant bit. If so, the speed difference is likely to be very minimal, but it still might be worth profiling things to check. Also,c /= 2
could be written asc >>>= 1
.
– Todd Lehman
Jul 29 '14 at 3:42
By the way, since this is a code review question, I would suggest a comment pointing the reader to en.wikipedia.org/wiki/Miller_rabin or some such page explaining where the list{ 2, 7, 61 }
comes from, and what the limitations are on your input.
– Todd Lehman
Jul 29 '14 at 3:56
1
@ToddLehman I'm sure Java optimizes the division by 2 even for signed operands, but it's slower. Together with the zero test it could be optimized without the adjustment, but I don't know i it happens.
– maaartinus
May 14 '15 at 0:27
Profile it and look where you spent most of the time. I would expect the modpow function., which is not that easy to further optimize.
– tb-
Apr 2 '13 at 13:16
Profile it and look where you spent most of the time. I would expect the modpow function., which is not that easy to further optimize.
– tb-
Apr 2 '13 at 13:16
perhaps
if (a < n) { continue;}
is better no jump to end of if statement before going to loop, but it would depends on compiler clerverness– cl-r
Sep 24 '13 at 6:28
perhaps
if (a < n) { continue;}
is better no jump to end of if statement before going to loop, but it would depends on compiler clerverness– cl-r
Sep 24 '13 at 6:28
Is
c % 2 == 1
in Java guaranteed to optimize down to the same as c & 1 == 0
? The reason I ask is because the type of c
is long
, which is signed, so this might force the JVM to use an actual divide-by-2-and-get-remainder operation rather than simply a boolean 'and' operation to extract the least-significant bit. If so, the speed difference is likely to be very minimal, but it still might be worth profiling things to check. Also, c /= 2
could be written as c >>>= 1
.– Todd Lehman
Jul 29 '14 at 3:42
Is
c % 2 == 1
in Java guaranteed to optimize down to the same as c & 1 == 0
? The reason I ask is because the type of c
is long
, which is signed, so this might force the JVM to use an actual divide-by-2-and-get-remainder operation rather than simply a boolean 'and' operation to extract the least-significant bit. If so, the speed difference is likely to be very minimal, but it still might be worth profiling things to check. Also, c /= 2
could be written as c >>>= 1
.– Todd Lehman
Jul 29 '14 at 3:42
By the way, since this is a code review question, I would suggest a comment pointing the reader to en.wikipedia.org/wiki/Miller_rabin or some such page explaining where the list
{ 2, 7, 61 }
comes from, and what the limitations are on your input.– Todd Lehman
Jul 29 '14 at 3:56
By the way, since this is a code review question, I would suggest a comment pointing the reader to en.wikipedia.org/wiki/Miller_rabin or some such page explaining where the list
{ 2, 7, 61 }
comes from, and what the limitations are on your input.– Todd Lehman
Jul 29 '14 at 3:56
1
1
@ToddLehman I'm sure Java optimizes the division by 2 even for signed operands, but it's slower. Together with the zero test it could be optimized without the adjustment, but I don't know i it happens.
– maaartinus
May 14 '15 at 0:27
@ToddLehman I'm sure Java optimizes the division by 2 even for signed operands, but it's slower. Together with the zero test it could be optimized without the adjustment, but I don't know i it happens.
– maaartinus
May 14 '15 at 0:27
|
show 1 more comment
6 Answers
6
active
oldest
votes
You could use the Sieve of Eratosthenes for numbers less than 10 million (you are only going up to 100 million).
I think that checking the low bit (num & 1) == 0
is quicker than checking the remainder num % 2 == 0
. Similarly, bit shifting is quicker for division or multiplication by 2: d /= 2
could be d >> 1
.
You might experiment with doing an initial check for division by a few low primes (3, 5, 7, 11) in your IsPrime method and see if that makes a difference.
Finally, for your "random" numbers, you might want to choose products of primes, e.g. 3*5*7*11*13..., or maybe that plus 1. Maybe that's crazy. But some "random" numbers might provide richer test cases than others, and cause a faster elimination of non-primes.
add a comment |
Java already has BigInteger.isProbablePrime(int certainty)
and BigInteger.nextProbablePrime()
. (There is no method to get the previous probable prime, though, so you'll need to get creative.)
That said, your Miller-Rabin implementation works remarkably quickly and accurately. Testing numbers up to 100 million, I found that it is 100% accurate in that range. To achieve 100% accuracy in that range with BigInteger.isProbablePrime()
, you would need a certainty
parameter of at least 9.
To obtain a list of primes within a range, you would be better off with the Sieve of Eratosthenes. For comparison, I tried generating a list of primes up to 100 million with three methods:
- Sieve of Eratosthenes (using @JavaDeveloper's implementation with bugfixes): 3 seconds
- Your
primes()
function: 37 seconds - Testing
BigInteger.valueOf(i).isProbablePrime(9)
fori
up to 100 million: ≈ 4 minutes
It's provably 100% accurate for numbers below 4,759,123,141. But sadly (as nobody noticed) it's totally broken for big numbers due to overflow.
– maaartinus
May 14 '15 at 0:17
add a comment |
In your millerRabin()
function, the calculation of s
and d
can be factored out of the loop, as they only depend on n
, not a
.
add a comment |
Several improvements: Consider that the test "if (p % 3 == 0)..." will identify 33.3% of all numbers as composite. If Miller-Rabin takes longer than three times to test whether p % 3 == 0, adding that test makes the code run faster on average. Same obviously with p % 5 == 0 and so on. You should probably check directly at least up to 100 or so.
For a 32 bit number, you'll do about 32 squaring operations modulo p. However, you can probably do at least four by just accessing a table. For example, when a = 2 any power up to 31 can be taken from a table. That saves probably 10% or more of the work.
add a comment |
Sadly nobody noticed that the whole computation is broken for big numbers due to overflow in
private static long modpow(long x, long c, long m) {
...
result = (result * aktpot) % m;
...
}
and
private static boolean millerRabin(long n) {
...
x = (x * x) % n;
...
}
I'd suggest changing the signature of
public static boolean IsPrime(long num)
to accept int
only. And obviously changing the name to isPrime
.
add a comment |
if i<2
(one test <) is better than isif i<=1
(= test and < test)- change
String ans="";
inStringBuilder ans=new StringBuilder();
- change
for (int i=0; i<arr.length;i++){
infor (int i=0,n=arr.length;i<n;i++){
- read Joshua Blosh "Effective Java" book
2
"if i<2
(one test<
) is better than isif i<=1
(=
test and<
test)" AFAIK this is completely false.<=
does not need to perform two comparisons, it will simply call a different conditional jump instruction at the machine level, which uses a slightly different circuit which takes exactly the same time to perform the comparison(keep in mind that if the difference in timing is too small then it's like if it were 0 since pipelines compute in steps ofclock_time/pipeline_steps
seconds.
– Bakuriu
Sep 24 '13 at 13:28
@Bakuriu ok! (I've not look how my Java's compiler do with <=) I was told about this optimization with Sybase requester.
– cl-r
Sep 24 '13 at 13:40
Forget about points 1 and 3. These are valid just for dumb compilers, Java can optimize such low level stuff itself.
– maaartinus
May 14 '15 at 0:22
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f24625%2fmiller-rabin-prime-test-speed-is-the-main-goal%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
You could use the Sieve of Eratosthenes for numbers less than 10 million (you are only going up to 100 million).
I think that checking the low bit (num & 1) == 0
is quicker than checking the remainder num % 2 == 0
. Similarly, bit shifting is quicker for division or multiplication by 2: d /= 2
could be d >> 1
.
You might experiment with doing an initial check for division by a few low primes (3, 5, 7, 11) in your IsPrime method and see if that makes a difference.
Finally, for your "random" numbers, you might want to choose products of primes, e.g. 3*5*7*11*13..., or maybe that plus 1. Maybe that's crazy. But some "random" numbers might provide richer test cases than others, and cause a faster elimination of non-primes.
add a comment |
You could use the Sieve of Eratosthenes for numbers less than 10 million (you are only going up to 100 million).
I think that checking the low bit (num & 1) == 0
is quicker than checking the remainder num % 2 == 0
. Similarly, bit shifting is quicker for division or multiplication by 2: d /= 2
could be d >> 1
.
You might experiment with doing an initial check for division by a few low primes (3, 5, 7, 11) in your IsPrime method and see if that makes a difference.
Finally, for your "random" numbers, you might want to choose products of primes, e.g. 3*5*7*11*13..., or maybe that plus 1. Maybe that's crazy. But some "random" numbers might provide richer test cases than others, and cause a faster elimination of non-primes.
add a comment |
You could use the Sieve of Eratosthenes for numbers less than 10 million (you are only going up to 100 million).
I think that checking the low bit (num & 1) == 0
is quicker than checking the remainder num % 2 == 0
. Similarly, bit shifting is quicker for division or multiplication by 2: d /= 2
could be d >> 1
.
You might experiment with doing an initial check for division by a few low primes (3, 5, 7, 11) in your IsPrime method and see if that makes a difference.
Finally, for your "random" numbers, you might want to choose products of primes, e.g. 3*5*7*11*13..., or maybe that plus 1. Maybe that's crazy. But some "random" numbers might provide richer test cases than others, and cause a faster elimination of non-primes.
You could use the Sieve of Eratosthenes for numbers less than 10 million (you are only going up to 100 million).
I think that checking the low bit (num & 1) == 0
is quicker than checking the remainder num % 2 == 0
. Similarly, bit shifting is quicker for division or multiplication by 2: d /= 2
could be d >> 1
.
You might experiment with doing an initial check for division by a few low primes (3, 5, 7, 11) in your IsPrime method and see if that makes a difference.
Finally, for your "random" numbers, you might want to choose products of primes, e.g. 3*5*7*11*13..., or maybe that plus 1. Maybe that's crazy. But some "random" numbers might provide richer test cases than others, and cause a faster elimination of non-primes.
edited Apr 2 '13 at 12:28
answered Apr 2 '13 at 12:01
GlenPetersonGlenPeterson
572414
572414
add a comment |
add a comment |
Java already has BigInteger.isProbablePrime(int certainty)
and BigInteger.nextProbablePrime()
. (There is no method to get the previous probable prime, though, so you'll need to get creative.)
That said, your Miller-Rabin implementation works remarkably quickly and accurately. Testing numbers up to 100 million, I found that it is 100% accurate in that range. To achieve 100% accuracy in that range with BigInteger.isProbablePrime()
, you would need a certainty
parameter of at least 9.
To obtain a list of primes within a range, you would be better off with the Sieve of Eratosthenes. For comparison, I tried generating a list of primes up to 100 million with three methods:
- Sieve of Eratosthenes (using @JavaDeveloper's implementation with bugfixes): 3 seconds
- Your
primes()
function: 37 seconds - Testing
BigInteger.valueOf(i).isProbablePrime(9)
fori
up to 100 million: ≈ 4 minutes
It's provably 100% accurate for numbers below 4,759,123,141. But sadly (as nobody noticed) it's totally broken for big numbers due to overflow.
– maaartinus
May 14 '15 at 0:17
add a comment |
Java already has BigInteger.isProbablePrime(int certainty)
and BigInteger.nextProbablePrime()
. (There is no method to get the previous probable prime, though, so you'll need to get creative.)
That said, your Miller-Rabin implementation works remarkably quickly and accurately. Testing numbers up to 100 million, I found that it is 100% accurate in that range. To achieve 100% accuracy in that range with BigInteger.isProbablePrime()
, you would need a certainty
parameter of at least 9.
To obtain a list of primes within a range, you would be better off with the Sieve of Eratosthenes. For comparison, I tried generating a list of primes up to 100 million with three methods:
- Sieve of Eratosthenes (using @JavaDeveloper's implementation with bugfixes): 3 seconds
- Your
primes()
function: 37 seconds - Testing
BigInteger.valueOf(i).isProbablePrime(9)
fori
up to 100 million: ≈ 4 minutes
It's provably 100% accurate for numbers below 4,759,123,141. But sadly (as nobody noticed) it's totally broken for big numbers due to overflow.
– maaartinus
May 14 '15 at 0:17
add a comment |
Java already has BigInteger.isProbablePrime(int certainty)
and BigInteger.nextProbablePrime()
. (There is no method to get the previous probable prime, though, so you'll need to get creative.)
That said, your Miller-Rabin implementation works remarkably quickly and accurately. Testing numbers up to 100 million, I found that it is 100% accurate in that range. To achieve 100% accuracy in that range with BigInteger.isProbablePrime()
, you would need a certainty
parameter of at least 9.
To obtain a list of primes within a range, you would be better off with the Sieve of Eratosthenes. For comparison, I tried generating a list of primes up to 100 million with three methods:
- Sieve of Eratosthenes (using @JavaDeveloper's implementation with bugfixes): 3 seconds
- Your
primes()
function: 37 seconds - Testing
BigInteger.valueOf(i).isProbablePrime(9)
fori
up to 100 million: ≈ 4 minutes
Java already has BigInteger.isProbablePrime(int certainty)
and BigInteger.nextProbablePrime()
. (There is no method to get the previous probable prime, though, so you'll need to get creative.)
That said, your Miller-Rabin implementation works remarkably quickly and accurately. Testing numbers up to 100 million, I found that it is 100% accurate in that range. To achieve 100% accuracy in that range with BigInteger.isProbablePrime()
, you would need a certainty
parameter of at least 9.
To obtain a list of primes within a range, you would be better off with the Sieve of Eratosthenes. For comparison, I tried generating a list of primes up to 100 million with three methods:
- Sieve of Eratosthenes (using @JavaDeveloper's implementation with bugfixes): 3 seconds
- Your
primes()
function: 37 seconds - Testing
BigInteger.valueOf(i).isProbablePrime(9)
fori
up to 100 million: ≈ 4 minutes
edited Apr 13 '17 at 12:40
Community♦
1
1
answered Mar 23 '14 at 19:03
200_success200_success
129k15152414
129k15152414
It's provably 100% accurate for numbers below 4,759,123,141. But sadly (as nobody noticed) it's totally broken for big numbers due to overflow.
– maaartinus
May 14 '15 at 0:17
add a comment |
It's provably 100% accurate for numbers below 4,759,123,141. But sadly (as nobody noticed) it's totally broken for big numbers due to overflow.
– maaartinus
May 14 '15 at 0:17
It's provably 100% accurate for numbers below 4,759,123,141. But sadly (as nobody noticed) it's totally broken for big numbers due to overflow.
– maaartinus
May 14 '15 at 0:17
It's provably 100% accurate for numbers below 4,759,123,141. But sadly (as nobody noticed) it's totally broken for big numbers due to overflow.
– maaartinus
May 14 '15 at 0:17
add a comment |
In your millerRabin()
function, the calculation of s
and d
can be factored out of the loop, as they only depend on n
, not a
.
add a comment |
In your millerRabin()
function, the calculation of s
and d
can be factored out of the loop, as they only depend on n
, not a
.
add a comment |
In your millerRabin()
function, the calculation of s
and d
can be factored out of the loop, as they only depend on n
, not a
.
In your millerRabin()
function, the calculation of s
and d
can be factored out of the loop, as they only depend on n
, not a
.
answered Mar 23 '14 at 19:05
200_success200_success
129k15152414
129k15152414
add a comment |
add a comment |
Several improvements: Consider that the test "if (p % 3 == 0)..." will identify 33.3% of all numbers as composite. If Miller-Rabin takes longer than three times to test whether p % 3 == 0, adding that test makes the code run faster on average. Same obviously with p % 5 == 0 and so on. You should probably check directly at least up to 100 or so.
For a 32 bit number, you'll do about 32 squaring operations modulo p. However, you can probably do at least four by just accessing a table. For example, when a = 2 any power up to 31 can be taken from a table. That saves probably 10% or more of the work.
add a comment |
Several improvements: Consider that the test "if (p % 3 == 0)..." will identify 33.3% of all numbers as composite. If Miller-Rabin takes longer than three times to test whether p % 3 == 0, adding that test makes the code run faster on average. Same obviously with p % 5 == 0 and so on. You should probably check directly at least up to 100 or so.
For a 32 bit number, you'll do about 32 squaring operations modulo p. However, you can probably do at least four by just accessing a table. For example, when a = 2 any power up to 31 can be taken from a table. That saves probably 10% or more of the work.
add a comment |
Several improvements: Consider that the test "if (p % 3 == 0)..." will identify 33.3% of all numbers as composite. If Miller-Rabin takes longer than three times to test whether p % 3 == 0, adding that test makes the code run faster on average. Same obviously with p % 5 == 0 and so on. You should probably check directly at least up to 100 or so.
For a 32 bit number, you'll do about 32 squaring operations modulo p. However, you can probably do at least four by just accessing a table. For example, when a = 2 any power up to 31 can be taken from a table. That saves probably 10% or more of the work.
Several improvements: Consider that the test "if (p % 3 == 0)..." will identify 33.3% of all numbers as composite. If Miller-Rabin takes longer than three times to test whether p % 3 == 0, adding that test makes the code run faster on average. Same obviously with p % 5 == 0 and so on. You should probably check directly at least up to 100 or so.
For a 32 bit number, you'll do about 32 squaring operations modulo p. However, you can probably do at least four by just accessing a table. For example, when a = 2 any power up to 31 can be taken from a table. That saves probably 10% or more of the work.
answered Mar 23 '14 at 15:41
gnasher729gnasher729
1,857611
1,857611
add a comment |
add a comment |
Sadly nobody noticed that the whole computation is broken for big numbers due to overflow in
private static long modpow(long x, long c, long m) {
...
result = (result * aktpot) % m;
...
}
and
private static boolean millerRabin(long n) {
...
x = (x * x) % n;
...
}
I'd suggest changing the signature of
public static boolean IsPrime(long num)
to accept int
only. And obviously changing the name to isPrime
.
add a comment |
Sadly nobody noticed that the whole computation is broken for big numbers due to overflow in
private static long modpow(long x, long c, long m) {
...
result = (result * aktpot) % m;
...
}
and
private static boolean millerRabin(long n) {
...
x = (x * x) % n;
...
}
I'd suggest changing the signature of
public static boolean IsPrime(long num)
to accept int
only. And obviously changing the name to isPrime
.
add a comment |
Sadly nobody noticed that the whole computation is broken for big numbers due to overflow in
private static long modpow(long x, long c, long m) {
...
result = (result * aktpot) % m;
...
}
and
private static boolean millerRabin(long n) {
...
x = (x * x) % n;
...
}
I'd suggest changing the signature of
public static boolean IsPrime(long num)
to accept int
only. And obviously changing the name to isPrime
.
Sadly nobody noticed that the whole computation is broken for big numbers due to overflow in
private static long modpow(long x, long c, long m) {
...
result = (result * aktpot) % m;
...
}
and
private static boolean millerRabin(long n) {
...
x = (x * x) % n;
...
}
I'd suggest changing the signature of
public static boolean IsPrime(long num)
to accept int
only. And obviously changing the name to isPrime
.
answered May 14 '15 at 0:20
maaartinusmaaartinus
12.3k12669
12.3k12669
add a comment |
add a comment |
if i<2
(one test <) is better than isif i<=1
(= test and < test)- change
String ans="";
inStringBuilder ans=new StringBuilder();
- change
for (int i=0; i<arr.length;i++){
infor (int i=0,n=arr.length;i<n;i++){
- read Joshua Blosh "Effective Java" book
2
"if i<2
(one test<
) is better than isif i<=1
(=
test and<
test)" AFAIK this is completely false.<=
does not need to perform two comparisons, it will simply call a different conditional jump instruction at the machine level, which uses a slightly different circuit which takes exactly the same time to perform the comparison(keep in mind that if the difference in timing is too small then it's like if it were 0 since pipelines compute in steps ofclock_time/pipeline_steps
seconds.
– Bakuriu
Sep 24 '13 at 13:28
@Bakuriu ok! (I've not look how my Java's compiler do with <=) I was told about this optimization with Sybase requester.
– cl-r
Sep 24 '13 at 13:40
Forget about points 1 and 3. These are valid just for dumb compilers, Java can optimize such low level stuff itself.
– maaartinus
May 14 '15 at 0:22
add a comment |
if i<2
(one test <) is better than isif i<=1
(= test and < test)- change
String ans="";
inStringBuilder ans=new StringBuilder();
- change
for (int i=0; i<arr.length;i++){
infor (int i=0,n=arr.length;i<n;i++){
- read Joshua Blosh "Effective Java" book
2
"if i<2
(one test<
) is better than isif i<=1
(=
test and<
test)" AFAIK this is completely false.<=
does not need to perform two comparisons, it will simply call a different conditional jump instruction at the machine level, which uses a slightly different circuit which takes exactly the same time to perform the comparison(keep in mind that if the difference in timing is too small then it's like if it were 0 since pipelines compute in steps ofclock_time/pipeline_steps
seconds.
– Bakuriu
Sep 24 '13 at 13:28
@Bakuriu ok! (I've not look how my Java's compiler do with <=) I was told about this optimization with Sybase requester.
– cl-r
Sep 24 '13 at 13:40
Forget about points 1 and 3. These are valid just for dumb compilers, Java can optimize such low level stuff itself.
– maaartinus
May 14 '15 at 0:22
add a comment |
if i<2
(one test <) is better than isif i<=1
(= test and < test)- change
String ans="";
inStringBuilder ans=new StringBuilder();
- change
for (int i=0; i<arr.length;i++){
infor (int i=0,n=arr.length;i<n;i++){
- read Joshua Blosh "Effective Java" book
if i<2
(one test <) is better than isif i<=1
(= test and < test)- change
String ans="";
inStringBuilder ans=new StringBuilder();
- change
for (int i=0; i<arr.length;i++){
infor (int i=0,n=arr.length;i<n;i++){
- read Joshua Blosh "Effective Java" book
answered Sep 24 '13 at 6:36
cl-rcl-r
858411
858411
2
"if i<2
(one test<
) is better than isif i<=1
(=
test and<
test)" AFAIK this is completely false.<=
does not need to perform two comparisons, it will simply call a different conditional jump instruction at the machine level, which uses a slightly different circuit which takes exactly the same time to perform the comparison(keep in mind that if the difference in timing is too small then it's like if it were 0 since pipelines compute in steps ofclock_time/pipeline_steps
seconds.
– Bakuriu
Sep 24 '13 at 13:28
@Bakuriu ok! (I've not look how my Java's compiler do with <=) I was told about this optimization with Sybase requester.
– cl-r
Sep 24 '13 at 13:40
Forget about points 1 and 3. These are valid just for dumb compilers, Java can optimize such low level stuff itself.
– maaartinus
May 14 '15 at 0:22
add a comment |
2
"if i<2
(one test<
) is better than isif i<=1
(=
test and<
test)" AFAIK this is completely false.<=
does not need to perform two comparisons, it will simply call a different conditional jump instruction at the machine level, which uses a slightly different circuit which takes exactly the same time to perform the comparison(keep in mind that if the difference in timing is too small then it's like if it were 0 since pipelines compute in steps ofclock_time/pipeline_steps
seconds.
– Bakuriu
Sep 24 '13 at 13:28
@Bakuriu ok! (I've not look how my Java's compiler do with <=) I was told about this optimization with Sybase requester.
– cl-r
Sep 24 '13 at 13:40
Forget about points 1 and 3. These are valid just for dumb compilers, Java can optimize such low level stuff itself.
– maaartinus
May 14 '15 at 0:22
2
2
"
if i<2
(one test <
) is better than is if i<=1
(=
test and <
test)" AFAIK this is completely false. <=
does not need to perform two comparisons, it will simply call a different conditional jump instruction at the machine level, which uses a slightly different circuit which takes exactly the same time to perform the comparison(keep in mind that if the difference in timing is too small then it's like if it were 0 since pipelines compute in steps of clock_time/pipeline_steps
seconds.– Bakuriu
Sep 24 '13 at 13:28
"
if i<2
(one test <
) is better than is if i<=1
(=
test and <
test)" AFAIK this is completely false. <=
does not need to perform two comparisons, it will simply call a different conditional jump instruction at the machine level, which uses a slightly different circuit which takes exactly the same time to perform the comparison(keep in mind that if the difference in timing is too small then it's like if it were 0 since pipelines compute in steps of clock_time/pipeline_steps
seconds.– Bakuriu
Sep 24 '13 at 13:28
@Bakuriu ok! (I've not look how my Java's compiler do with <=) I was told about this optimization with Sybase requester.
– cl-r
Sep 24 '13 at 13:40
@Bakuriu ok! (I've not look how my Java's compiler do with <=) I was told about this optimization with Sybase requester.
– cl-r
Sep 24 '13 at 13:40
Forget about points 1 and 3. These are valid just for dumb compilers, Java can optimize such low level stuff itself.
– maaartinus
May 14 '15 at 0:22
Forget about points 1 and 3. These are valid just for dumb compilers, Java can optimize such low level stuff itself.
– maaartinus
May 14 '15 at 0:22
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f24625%2fmiller-rabin-prime-test-speed-is-the-main-goal%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Profile it and look where you spent most of the time. I would expect the modpow function., which is not that easy to further optimize.
– tb-
Apr 2 '13 at 13:16
perhaps
if (a < n) { continue;}
is better no jump to end of if statement before going to loop, but it would depends on compiler clerverness– cl-r
Sep 24 '13 at 6:28
Is
c % 2 == 1
in Java guaranteed to optimize down to the same asc & 1 == 0
? The reason I ask is because the type ofc
islong
, which is signed, so this might force the JVM to use an actual divide-by-2-and-get-remainder operation rather than simply a boolean 'and' operation to extract the least-significant bit. If so, the speed difference is likely to be very minimal, but it still might be worth profiling things to check. Also,c /= 2
could be written asc >>>= 1
.– Todd Lehman
Jul 29 '14 at 3:42
By the way, since this is a code review question, I would suggest a comment pointing the reader to en.wikipedia.org/wiki/Miller_rabin or some such page explaining where the list
{ 2, 7, 61 }
comes from, and what the limitations are on your input.– Todd Lehman
Jul 29 '14 at 3:56
1
@ToddLehman I'm sure Java optimizes the division by 2 even for signed operands, but it's slower. Together with the zero test it could be optimized without the adjustment, but I don't know i it happens.
– maaartinus
May 14 '15 at 0:27