问题
I read lot many algorithms to find prime numbers and the conclusion is that a number is a prime number if it is not divisible by any of its preceding prime numbers.
I am not able to find a more precise definition. Based on this I have written a code and it performs satisfactory till the max number I pass is 1000000. But I believe there are much faster algorithms to find all primes lesser than a given number.
Following is my code, can I have a better version of the same?
public static void main(String[] args) {
for (int i = 2; i < 100000; i++) {
if (checkMod(i)) {
primes.add(i);
}
}
}
private static boolean checkMod( int num) {
for (int i : primes){
if( num % i == 0){
return false;
}
}
return true;
}
回答1:
The good thing in your primality test is that you only divide by primes.
private static boolean checkMod( int num) {
for (int i : primes){
if( num % i == 0){
return false;
}
}
return true;
}
The bad thing is that you divide by all primes found so far, that is, all primes smaller than the candidate. That means that for the largest prime below one million, 999983, you divide by 78497 primes to find out that this number is a prime. That's a lot of work. So much, in fact, that the work spent on primes in this algorithm accounts for about 99.9% of all work when going to one million, a larger part for higher limits. And that algorithm is nearly quadratic, to find the primes to n in this way, you need to perform about
n² / (2*(log n)²)
divisions.
A simple improvement is to stop the division earlier. Let n be a composite number (i.e. a number greter than 1 that has divisors other than 1 and n), and let d be a divisor of n.
Now, d being a divisor of n means that n/d is an integer, and also a divisor of n: n/(n/d) = d.
So we can naturally group the divisors of n into pairs, each divisor d gives rise to the pair (d, n/d).
For such a pair, there are two possibilities:
d = n/d, which meansn = d², ord = √n.- The two are different, then one of them is smaller than the other, say
d < n/d. But that immediately translates tod² < nord < √n.
So, either way, each pair of divisors contains (at least) one not exceeding √n, hence, if n is a composite number, its smallest divisor (other than 1) does not exceed √n.
So we can stop the trial division when we've reached √n:
private static boolean checkMod( int num) {
for (int i : primes){
if (i*i > n){
// We have not found a divisor less than √n, so it's a prime
return true;
}
if( num % i == 0){
return false;
}
}
return true;
}
Note: That depends on the list of primes being iterated in ascending order. If that is not guaranteed by the language, you have to use a different method, iterate by index through an ArrayList or something like that.
Stopping the trial division at the square root of the candidate, for the largest prime below one million, 999983, we now only need to divide it by the 168 primes below 1000. That's a lot less work than previously. Stopping the trial division at the square root, and dividing only by primes, is as good as trial division can possibly get and requires about
2*n^1.5 / (3*(log n)²)
divisions, for n = 1000000, that's a factor of about 750, not bad, is it?
But that's still not very efficient, the most efficient methods to find all primes below n are sieves. Simple to implement is the classical Sieve of Eratosthenes. That finds the primes below n in O(n*log log n) operations, with some enhancements (eliminating multiples of several small primes from consideration in advance), its complexity can be reduced to O(n) operations. A relatively new sieve with better asymptotic behaviour is the Sieve of Atkin, which finds the primes to n in O(n) operations, or with the enhancement of eliminating the multiples of some small primes, in O(n/log log n) operations.
The Sieve of Atkin is more complicated to implement, so it's likely that a good implementation of a Sieve of Eratosthenes performs better than a naive implementation of a Sieve of Atkin. For implementations of like optimisation levels, the performance difference is small unless the limit becomes large (larger than 1010; and it's not uncommon that in practice, a Sieve of Eratosthenes scales better than a Sieve of Atkin beyond that, due to better memory access patterns). So I would recommend beginning with a Sieve of Eratosthenes, and only when its performance isn't satisfactory despite honest efforts at optimisation, delve into the Sieve of Atkin. Or, if you don't want to implement it yourself, find a good implementation somebody else has already seriously tuned.
I have gone into a bit more detail in an answer with a slightly different setting, where the problem was finding the n-th prime. Some implementations of more-or-less efficient methods are linked from that answer, in particular one or two usable (though not much optimised) implementations of a Sieve of Eratosthenes.
回答2:
I always use Eratosthenes sieve:
isPrime[100001] // - initially contains only '1' values (1,1,1 ... 1)
isPrime[0] = isPrime[1] = 0 // 0 and 1 are not prime numbers
primes.push(2); //first prime number. 2 is a special prime number because is the only even prime number.
for (i = 2; i * 2 <= 100000; i++) isPrime[i * 2] = 0 // remove all multiples of 2
for (i = 3; i <= 100000; i += 2) // check all odd numbers from 2 to 100000
if (isPrime[i]) {
primes.push(i); // add the new prime number to the solution
for (j = 2; i * j <= 100000; j++) isPrime[i * j] = 0; // remove all i's multiples
}
return primes
I hope you understand my comments
回答3:
I understand a prime number to be a number that is only divisible by itself and the number 1 (with no remainder). See Wikipedia Article
That being said, I don't understand the algorithm very well in the second comment but one small improvement to your algorithm would be to change your for loop to:
for (int i = 5; i < 100000; i = i + 2) {
if (checkMod(i)) {
primes.add(i);
}
}
This is based on the assumption that 1, 2, and 3 are all prime numbers and all even numbers thereafter are not prime numbers. This at least cuts your algorithm in half.
回答4:
I want to make a still slightly improved version to the 0ne suggested by Benjamin Oman above, This is just one modification to avoid checking for primality of all the numbers ending with digit '5', because these numbers are certainly not primes as these are divisible by 5.
for (int i = 7;(i < 100000) && (!i%5==0); i = i + 2) {
if (checkMod(i)) {
primes.add(i);
}
}
This is based on the assumption that 2,3,5 are primes. The above little change will reduce all factors of 5 and improve.
回答5:
Nicely explained by @Daniel Fischer.
A Implementation in C++ from his explanation:
#include<iostream>
using namespace std;
long* getListOfPrimeNumbers (long total)
{
long * primes;
primes = new long[total];
int count = 1;
primes[0] = 2;
primes[1] = 3;
while (count < total)
{
long composite_number = primes[count] + 2;
bool is_prime = false;
while (is_prime == false)
{
is_prime = true;
for (int i = 0; i <= count; i++)
{
long prime = primes[i];
if (prime * prime > composite_number)
{
break;
}
if (composite_number % prime == 0)
{
is_prime = false;
break;
}
}
if (is_prime == true)
{
count++;
primes[count] = composite_number;
}
else
{
composite_number += 2;
}
}
}
return primes;
}
int main()
{
long * primes;
int total = 10;
primes = getListOfPrimeNumbers(total);
for (int i = 0; i < total; i++){
cout << primes[i] << "\n";
}
return 0;
}
来源:https://stackoverflow.com/questions/10690843/finding-the-list-of-prime-numbers-in-shortest-time