2

I'm trying to make the fastest algorithm to find out if a number is prime. I'm doing this for each number from 3 to 100,000.

   for(int i = 3; i < 100000; i += 1)
        if(isPrime(i))
            System.out.println(i);

and it takes 0.52 seconds. My friend suggested to not iterate for even numbers:

for(int i = 3; i < 100000; i += 2)
        if(isPrime(i))
            System.out.println(i);

and it takes 0.53 seconds (probably a random difference).

Why doesn't his suggestion reduce the runtime? if I iterate through less numbers I'd expect that the program will run faster.

Code of isPrime():

public static boolean isPrime(int n)
{
    if((n % 2 == 0 && n != 2) || (n % 3 == 0  && n != 3)|| (n % 5 == 0 && n != 5))
        return false;
    for(int i = 5; i < n / 5; i += 2)
    {
        if(n % i == 0)
            return false;
    }
    return true;
}
phant0m
  • 16,595
  • 5
  • 50
  • 82
shohamh
  • 317
  • 1
  • 5
  • 15
  • 1
    See related question http://stackoverflow.com/questions/453793/which-is-the-fastest-algorithm-to-find-prime-numbers – Mihai8 Feb 28 '13 at 12:49
  • Find the prime number of sqrt(MAX_INPUT) with seiving, and test on all the prime number from 2 to sqrt(MAX_INPUT). But if you are searching for prime number within a big range, then just seive up to MAX_INPUT. – nhahtdh Feb 28 '13 at 12:50
  • 2
    @nhahtdh I know that solution, but may I ask why it helps? – shohamh Feb 28 '13 at 12:51
  • 1
    @shohamh: It does less work, that is why it is faster. (I only discuss about algorithm here. Printing may take more time than the algorithm itself in this case). – nhahtdh Feb 28 '13 at 12:55
  • @nhahtdh The question is about why testing 50000 numbers and testing 100000 numbers takes the same time... It should logically take less time... (The algorithm is under scrutiny here) – RudolphEst Feb 28 '13 at 13:05
  • 2
    @RudolphEst: testing 100000 numbers is nothing these days. Writing to the console is a much more of an issue... – ppeterka Feb 28 '13 at 13:11
  • *"I'm trying to make the fastest algorithm to find out if a number is prime"* - for moderately large numbers, sieving is unrealistic. Mid-to-large numbers typically use a probablistic algorithm like [Miller-Rabin](http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test), which FYI is already [included in Java](http://docs.oracle.com/javase/1.4.2/docs/api/java/math/BigInteger.html#isProbablePrime%28int%29). – BlueRaja - Danny Pflughoeft Feb 28 '13 at 17:08

6 Answers6

9

Most probably the bulk of the time is needed for printing out the primes to the console. Thus reducing the numbers tested won't notably affect the speed of the program, if the number of primes is not reduced as well.

Try to collect the primes into a string and print that out once, like this:

StringBuilder b = new StringBuilder();
for(int i = 3; i < 100000; i += 1)
    if(isPrime(i))
      b.append(i).append("\n");

System.out.println(b.toString());
Thomas
  • 87,414
  • 12
  • 119
  • 157
  • +1 That explains the effect `System.out` might have on the outputs. –  Feb 28 '13 at 12:53
4

Apart from algorithmical issues, your bottleneck is most likely writing to the System.out stream. That is a time-consuming task, I think you should leave that part out of the benchmark loop. To quickly test this, just comment it out (with the corresponding if statement):

int iterations=100000;
long time = System.nanoTime();
for(int i = 3; i < 100000; i += 2) { //notice: always use curly brackets!!!
    isPrime(i);
}
long endTime = System.nanoTime();
System.out.println("Time to go through " + iterations + " iterations: " + (endTime>time?endTime-time:endTime+time));
//notice: nanoTime might turn around, resulting in smaller (negative) endTime value

Also, the answer of Thomas is much more detailed regarding the System.out.print issue, and also provides the appropriate approach of concatenating a lot of strings.

Algorithmic problems:

If you are going with a sieve of Erastothenes approach, you already have found all the smaller primes when searching for the next one. So you should store them, and instead of checking each and every odd number >5, you only have tocheck those that you already have.

Also, at the same time, you don't have to check all of them, only those, that are smaller than, or equal to the square root of your number:

//suppose we have a List<Integer> primeList (populated by previous execution loops)
// and Integer numberTested as the number under testing

for(int i=0; i<primeList.size();i++) {
    if(numberTested%primeList.get(i)==0) {
        //divider found, not prime
        break;
    }        

    if(primeList.get(i)>Math.sqrt(numberTested)) {
        //still not found a divider -- we found a prime
        primeList.add(numberTested);
        break;
    }
}  

What is the problem with this? Math.sqrt is a costly operation. Much costlier than multiplication... So if the range of numbers permits (you have to have that always in mind!), we can use multiplication to get the comparison quicker:

sqrt(a)>b === a*a>b given that both a and b are positive integers.

    Integer currentPrimeSquare = primeList.get(i)*primeList.get(i);
    if(currentPrimeSquare>numberTested) {
        //still not found a divider -- we found a prime
        primeList.add(numberTested);
        break;
    }

To further tune this, you could store the squares of the primes too - given you have enough memory....

ppeterka
  • 20,583
  • 6
  • 63
  • 78
1

There is the overhead of VM itself to worry about when comparing java runtimes (println can take different periods at every execution for instance). To get a proper comparison of runtimes, run each one a thousand times and get average values for more accurate results.

Also, implementing the Sieve of Erastothenes in Java is not that difficult an will give you very quick results too, though it will use more memory.

RudolphEst
  • 1,240
  • 13
  • 21
0

If you're interested in the most efficient algorithm known to date whether a given number is prime, you should consider reading about the AKS primality test, which runs in polylogarithmic time.

(This answer basically is a reiteration of another answer to a question regarding primality tests.)

Community
  • 1
  • 1
G. Bach
  • 3,869
  • 2
  • 25
  • 46
0

1) did you consider to use Sieve of Eratosthenes for finding primes?

2) you shouldn't really measure efficiency by using running time

3) your if statement in is prime, if you are checking is n = {2,3,5} use it to return result, that will prevent you from executing rest of code (not much time saver in this case)

4) your for loop, you can do it for i from 7 (condition for 5 you already check, and 6 is not prime number) to sqrt(n)

user902383
  • 8,420
  • 8
  • 43
  • 63
-2

Maybe this article can help you:

boolean isPrime(int n) {
//check if n is a multiple of 2
if (n%2==0) return false;
//if not, then just check the odds
for(int i=3;i*i<=n;i+=2) {
    if(n%i==0)
        return false;
}
return true;
}
Twinone
  • 2,949
  • 4
  • 28
  • 39
  • 2
    How does that address the actual question? You know, the part of the OP's text with the question mark behind it? – arne.b Feb 28 '13 at 12:56
  • 3
    This "improved" version reports that 2 is __not__ a prime number. Speed doesn't matter if it gives wrong answers... – Blastfurnace Feb 28 '13 at 16:49