1

I had to write a simple program to print the prime numbers from 2 until 100.

First I did some research about what a prime number is. I tried for a long time, and finally i looked up the answer in the book, sinceIi did not succeed every time writing a 100 % working code.

I understand the answer code for the most part, but one part I don't understand. Let me try to explain. First here is the code from the book:

// Find prime numbers between 2 and 100.
class Prime {

    public static void main(String args[]) {
        int i, j;
        boolean isprime;
        for(i=2; i < 100; i++) {
            isprime = true;
            // see if the number is evenly divisible
            for(j=2; j < i/j; j++)
               // if it is, then it's not prime
               if((i%j) == 0) isprime = false;
            if(isprime)
               System.out.println(i + " is prime.");
        }
    }
}

Ok, so I know this: a prime number can only be divided by itself and by 1.

Let me take number 4 first, this is not a prime, since it can also be divided by 2. So in the code I am following the for loops, but I am stuck at the 'see if the number is evenly divisible' part. In the case of 4, then 4%4 has no remainder, so it is false because of the ==0 part.

So isprime is then false. However, I am reading it wrong or thinking wrong, because if a take a prime number, like 5, i get the same: 5%5 has no remainder, because 5/5 == 1. So in this case 5%5 is also equal to 0, so the isprime should also be false, but the number 5 is a prime number in this case.

So I don't really understand how this check works in the code.

In the begining i is 2 and j is 2, so you get 2%2, also no remainder, but 2 is also a prime number, so I know I am seeing it wrong somewhere.

If someone can explain how this works exactly, I was searching for an hour on the web but could not find it.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
Shibito
  • 11
  • 3

5 Answers5

1

First, it should be said - and already mentioned in other answers - that the code you got from the book is wrong (or maybe you missed out copying it). The condition of the internal loop is supposed to be

for(j=2; j <= i/j; j++)

one important lesson from this is that you have to try running even book examples, to see if they work. If you ran this, you would have seen that it marks 4,9,25,49 as prime, when they are not.

But your reading of the code was also not correct. You said that 4 is judged to be not prime because 4%4 == 0. But in fact, a prime number is allowed to be divisible by itself (any number is). So indeed, testing for a prime number by dividing it by itself would not work.

Let's see what this is all about. You understand the outer loop - it is giving you the candidates to test whether they are prime or not.

Then it works like this: assume the current candidate is prime. Try to divide it by all possible divisors. If any of the divisors divides it evenly, mark it as "not prime".

If, after all the possible divisors were tested, none of them got to the point that marks it as "not prime", then it is indeed prime and you can print it.

So the i loop represents the candidates, and the j loop represents the potential divisors. Take the number 15 as an example of i.

  • Start j with 2. i/j is 15/2 which is 7. 2 < 7, so we go inside the loop.
    • Is 2 a divisor of 15? No, it isn't. Nothing is done.
  • j is now 3. i/j is 15/3 which is 5. 3 < 5, so we continue.
    • Is 3 a divisor of 15? Yes! So set isprime=false.
  • j is now 4. i/j is 15/4 which is 3. 4 < 3 is false, so we stop.

As you can see, the step with j=3 above set isprime as false, so we do not print it - it is not prime.

Now let's take a real prime number, like 13:

  • Start j with 2. i/j is 13/2 which is 6. 2 < 6, so we go inside the loop.
    • Is 2 a divisor of 13? No, it isn't. Nothing is done.
  • j is now 3. i/j is 13/3 which is 4. 3 < 4, so we continue.
    • Is 3 a divisor of 13? No, it isn't. Nothing is done.
  • j is now 4. i/j is 13/4 which is 3. 4 < 3 is false, so we stop.

None of the steps set isprime to false. It is still true, so the number 13 is prime and we print it.

Now, the trick here is which candidates we offer for j. Naively, a person writing this the first time would think that the candidates should be all the numbers between 1 and i, excluding 1 and i themselves. So they would write a loop like:

for ( j = 2; j < i; j++ )

But this is wasteful. Why? Suppose you checked the number 15. You found that 3 is a divisor for it. How is that? Because 15 = 3 x 5. But this means that 5 is also a divisor. You don't need to test the divisor which, if you divide by it, you get a divisor that you already tested. So no need to test 5, because we have already tested 3.

So, again, naive programmers might decide that we can just test half the candidates:

for ( j = 2; j <= i / 2; j++ )

But in fact, mathematically speaking, this is still wasteful. Really, we should go only up to the square root of i. Why? Because if a divisor j is bigger than the square root of i, such that j x k = i, then k will be smaller than the square root of i. If k was bigger, then j x k would be bigger than sqrt(i) x sqrt(i) which means they j x k > i. But we know that they are equal to i!

This means that if there is a potential divisor greater than the square root of i, we already found the other divisor, smaller than the square root of i, and tested it and marked i as "not prime".

So this is what your candidate loop is testing. It's basically a simple way of writing

for ( j = 2; j <= Math.sqrt(i); j++ )

Without calling the Math class and without converting the integers to doubles.

But as I said from the start, it's important that the condition is <= rather than <. For numbers which are squares of primes, (4 = 2 x 2, 9 = 3 x 3), the only proper divisor is the number that is equal to their square root.


One last note: this book example is still wasteful, because it doesn't stop checking after it found one divisor that makes the number not prime. Finding one divisor is enough. One way to do this is to change the loop condition like this:

for ( j = 2; isprime && j <= i/j; j++ )

So the loop will only continue as long as we still have reason to believe that the number is prime. After a j that marks isprime as false, the loop will automatically stop.

(And of course, this algorithm is not the best algorithm. There are better algorithms. It's just the one everybody starts with).


Answers to questions raised in the comments:

An inner loop is always executed afresh. Think of it like this:

If a loop says:

for ( int i = 0; i < 3; i++ ) {
     doSomething();
}

It's equivalent to

doSomething();
doSomething();
doSomething();

So if the "do something" part is in itself a loop:

for ( int i = 0; i < 3; i++ ) {
    for ( int j = 0; j < 2; j++ ) {
        runSomething();
    }
}

is equivalent to:

for ( int j = 0; j < 2; j++ ) {
    runSomething();
}
for ( int j = 0; j < 2; j++ ) {
    runSomething();
}
for ( int j = 0; j < 2; j++ ) {
    runSomething();
}

So you see, each time it's a new j that starts from zero.

As for the question about the curly braces: officially, the format of a for statement is:

for ( initialization; termination; increment )
    statement

The statement can be a single statement, like a method call, an inner for loop, an if statement, or something. In that case, it doesn't need braces. Or the statement can be a block, which is a pair of braces that contains zero or more statements. You are used to seeing a block, and indeed it's recommended to always use a block, not a single statement, as it is easier to add statements.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • Thanks for the great answers. I am feeling so stupid, i forgot that an inner for loop keeps on going as long as it is true, before returning to the outer for loop. Also, of course j stays 2 if it is false. So it checks if j is lower then i divided by j, if yes, then it checks if there is a remainder with i%j, if there is no remainder, then it is not a prime. I see the problem with 4 now, since 2 is not lower then 2, it still prints 4 as an prime, which is wrong. What i don't get it, why is the inner for loop not between {}? I tried this, and this also works. I'll try the <= part. Many thanks. – Shibito Apr 14 '15 at 16:50
  • I added the <= and now it does not print the 4. I am getting this more and more, but i am confused about when j is getting 3. Does it also reset to 2 somewhere, because there is no count reset? Why it does not print 6? Because, 4/2 = 2, and j is then equal to 2 and true, so j is now 3. 5/3 = 1, j is 3, so 3 <= 1 is false, j stays 3. But now 6, 6/3 = 2. Since j is still 3, 3 <= 6 is also false, so the inner for loop stops and should print 6 as a prime, but it is not a prime. I am dizzy and feeling stupid now, what am i reading or thinking wrong here? – Shibito Apr 14 '15 at 17:15
  • @Shibito - the loop in `j` is an *inner* loop. For every iteration of the outer loop, the inner loop begins again from the start, with `j` <= 2. The loop doesn't stop until `j` is bigger than the square root of `i`. But for the next candidate, `j` starts afresh from 2. – RealSkeptic Apr 14 '15 at 17:51
  • So i understand now that within the inner j loop, j gets 3 as soon if the <= i/j part is true. I also understand that if the inner j loop ends false it stops incrementing j. And if i in the outer loop gets incremented again, j is in fact reset to 2 when the j loop is starting again. Is this a common thing that a variabel is starting at his initialization in an inner loop? Because i thought that initialization was only once in a for loop? Also, i still don't get why the inner loop is not enclosed within { and }. Is this not a better code with these { and }? – Shibito Apr 15 '15 at 06:09
  • @Shibito I added a little bit at the end of my answer to answer your questions. If you want to deepen your knowledge about all this, perhaps you should read the Java Language Specification. – RealSkeptic Apr 15 '15 at 07:46
  • Thanks again. Sorry for all the questions, i've read the post a hunderd times, lol. I have a lot of patience, but sometime i think to difficult or i'm learning slower. Anyway, what i still don't seem to get, once it reaches the if((i%j) == 0) isprime = false; part and the if statement is false (so with a remainder) then it starts the j loop again with j incremented. But why does it also skip the next if statement with the println statement? – Shibito Apr 15 '15 at 11:06
  • @Shibito how many statements are there inside the body of the `j` loop? – RealSkeptic Apr 15 '15 at 11:51
  • For what i can see three, both if statements and the println statement – Shibito Apr 15 '15 at 12:16
  • @Shibito, no. As I said in my added text, the only statement in the `for` is the statement that immediately follows it, unless there are curly braces that hold more than one statement. This is also why, when I edited your question, I indented the text the way I did. – RealSkeptic Apr 15 '15 at 12:18
  • Thank you so much, i feel embarrassed with all my questions. Sorry for that. I tried to find this in the book, but could only find that statements within the curly braces are skipped or executed. Could not find that if a single statement follows a for only this is excecuted. So in short, the for j loop only has one statement, if this statement is true, isprime gets false, and the for j loop stops if j <= i/j is false. And of course the other way around. Thank you so much and srry again for me being so stupid :( – Shibito Apr 15 '15 at 13:17
0

You have a small error in your inner loop header.

// ...
for(j=2; j <= i/j; j++)
// ...

So instead of < you have to use <= .

Timo Hanisch
  • 224
  • 1
  • 7
0

The Best way to try to understand this, it's putting System.out.println inside the loop with the values of i, j.

I do it for you with, 2, 3,4, and 5. (only 4 is not prime).

with i = 2 -> condition of the loop is 2 < 2/2 is false, you don't go inside the loop, it's prime.

with i = 3 -> condition of the loop is 2 < 3/2 is false, you don't go inside the loop, it's prime.

with i = 4 -> condition of the loop is 2 < 4/2 is false, you don't go inside the loop and it's prime. maybe the loop condition should be <=

with i = 5 -> condition of the loop is 2 < 5/2 is true, you go inside the loop, and the if condition is false, you add 1 to j and do the loop: 3 < 5/3 is false, you don't go inside the loop, it's prime.

maiklahoz
  • 135
  • 8
0

Your problem is in the second for loop

for(j=2; j < i/j; j++)

For 4 and below, the control never enters the inner for loop and you have assumed your number to be prime before you begin checking it, so if the control never enters the loop, it is treated to be prime.

The condition should have been:

for(j=2; j <= i/j; j++)

this will give you the correct result.

Manish Kothari
  • 1,702
  • 1
  • 24
  • 34
0

I just got done working out the same problem. while it ended up taking a few hours, I forced myself not to look for an answer anywhere but the material preceding the question. I still have no idea what the solution says in the book. My point is try to fight through the aggravation, review the material at hand and you'll get it. Here's my code, ugly but affective.

class primeNum {

public static void main(String args[]){

    int x = 2;
    int y = 2;
    int count;
    System.out.println("The prime numbers between 2 and 100 are: ");

    while (x < 100) {
        count = 0;
        y = 2;

        while (y <= x) {

            if (x % y == 0)  {
                count ++;
            }
        ++y;
        }   
                if (count == 1 ) {
                    System.out.println(" " + x);
            }

        ++x;
    }
}

}