Reputation: 13
I was working on an algorithm in Java to find all primes up to a certain number. It was supposed to be an improvement of an earlier method of doing so, which was the following:
public static int[] generatePrimesUpTo(int max)
{
int[] primes = new int[max];
primes[0]=2;
int p = 1;
for (int i=3;i<max;i+=2)
{
if (isPrime(i))
{
primes[p]=i;
p+=1;
}
}
return primes;
}
public static boolean isPrime(int a)
{
for (int i=3;i<((int)Math.sqrt(a)+1);i+=2)
{
if (a%i==0)
return false;
}
return true;
}
Which just checks for a number N wether or not it is divisible by a smaller number, starting at 2 and ending at sqrt(N).
Now the new approach was to divide N only by smaller prime numbers which the algorithm had found earlier. I thought it would speed up the process quite a lot since it would have to do a lot less calculations.
public static int[] generatePrimes(int num)
{
int[] primes = new int[num];
int p = 3;
primes[0] = 2;
primes[1] = 3;
primes[2] = 5;
boolean prime;
for (int i=7;i<num;i+=2)
{
prime = true;
for (int j=0;primes[j+1]<(Math.sqrt(i)+1);j++)
{
if (i%primes[j]==0)
{
prime = false;
break;
}
}
if (prime)
{
primes[p]=i;
p++;
}
}
return primes;
}
However, there seems to be almost no difference in speed for Nmax = 10^7. For Nmax = 10^8 the new one was 20% faster, but my computer was more active during the calculation of the old one and I tried 10^8 only once.
Could anyone tell me about why this new approach isn't that much faster? Or what I could do to improve the algorithm more?
Thanks in advance!
Upvotes: 1
Views: 55
Reputation: 3316
Your second algorithm is faster. I don't know why you only saw a 20% improvement. Here are the results of my tests, with separate implementations:
10^6:
First: 00:00:01.0553813 67240405 steps
Second: 00:00:00.2416291 13927398 steps
Sieve: 00:00:00.0269685 3122044 steps
10^7:
First: 00:00:26.4524301 1741210134 steps
Second: 00:00:04.6647486 286144934 steps
Sieve: 00:00:00.3011046 32850047 steps
10^8:
First: 00:12:00.8986644 46474124250 steps
Second: 00:01:43.1543445 6320928466 steps
Sieve: 00:00:03.6146328 342570200 steps
The last algorithm was the Sieve of Eratosthenes which is a much better algorithm. I implemented all of these in C# for one processor, with the first two based on your code with minor changes like testing primes[j]*primes[j] <= i
.
The implementation of the Sieve of Eratosthenes I used was pretty basic,
Boolean[] definitelyComposite = new Boolean[max]; // initialized automatically to false
int p = 0;
for (long i = 2; i < max; i++)
{
numSteps++;
if (!definitelyComposite[i])
{
primes[p] = i;
p++;
for (long j = i * i; j < max; j += i)
{
numSteps++;
definitelyComposite[j] = true;
}
}
}
and it could be improved. For example, except for when i is 2, I could have used j+= 2*i
in the loop.
Upvotes: 0
Reputation: 52538
You should think about whether there isn't a method to find all primes in a range that is much faster than checking each prime individually. For example, you will check many numbers whether they are divisible by 73. But the fact is, that you can much faster determine all the numbers divisible by 73 (they are 73, 2*73, 3*73, 4*73 etc.).
By the way: You calculate Math.sqrt (j) in each single iteration of the loop. Moving that calculation outside the loop might make your code considerably faster.
Upvotes: 1