Variables
Operators
Expressions and Statements
Once we start putting variables and operators together, we can build parts of a
program. Evaluating these parts give us an output. But these aren't necessarily
the final output, they could just be intermediate outputs that we put into
another process to get more outputs.
| > | n1 | n2 | n3 |
| max is n1 | ? | ? | |
| max is n2 | ? | ? | |
| max is n3 | ? | ? |
The way to read this table is to look at each row and the comparisons we need to check for each of them. In the Java program, we want to fill in the "?" whenever we find out whether a certain number is bigger than another one or not. Take the question marks on the second row for instance (the two highlighted in colour). The one on the left checks to see whether "n2 > n1" and the one on the right checks whether "n2 > n3". Furthermore, the boxes along the diagonal are empty because we don't ever bother checking a number with itself. So we just ignore those boxes.
Now, we consider each line of the program in Version 1. The first line:
"if( n1 > n2 && n2 > n3 )"This is only true with the following table:
| > | n1 | n2 | n3 |
| max is n1 | yes | yes | |
| max is n2 | ? | ? | |
| max is n3 | ? | ? |
This immediately tells us that n1 is the max of all 3 numbers, so we don't need to check any further. That's why line 2 of the program is:
max = n1;
Thus, when line 1 is true, we won't do the other checks because they all have "else" in front of them. "else" is only used when something is false. Now you can probably see why the rest of the program has two other checks. In line 4, we check whether n2 is bigger than the other two numbers (to fill in those question marks in the second row in the table), and in line 7, we check whether n3 is bigger than the other two numbers (to fill in those question marks in the third row of the table). In each of the cases, when the checks return true, then we know exactly which number is the biggest. This is why Version 1 is written this way.
That's not all there is to understanding this program though. What if one of the checks is false?
Note however, that if in fact the first line is false, we don't actually know what went wrong. Because we are check for two things are the same time, it could be that "n1 > n2" is false, or that "n2 > n3" is false. So if the program goes to the "else" part, we need to update the table in the following way:
| > | n1 | n2 | n3 |
| max is n1 | ?/no | ?/no | |
| max is n2 | ? | ? | |
| max is n3 | ? | ? |
So now we go onto check the next set of conditions in line 4. By the same logic, if n2 is bigger than both n1 and n3, then we are done and we can update the table as follows:
| > | n1 | n2 | n3 |
| max is n1 | ?/no | ?/no | |
| max is n2 | yes | yes | |
| max is n3 | ? | ? |
In this case, max is n2. But again, if something goes wrong, then it could be that "n2 > n1" is false, or "n2 > n3" is false, or they were both false. So if line 4 fails, then the alternative table looks like:
| > | n1 | n2 | n3 |
| max is n1 | ?/no | ?/no | |
| max is n2 | ?/no | ?/no | |
| max is n3 | ? | ? |
So far, we have failed two checks (lines 1 and 4) and because there are multiple conditions in the same line, we couldn't tell which conditions caused the problem. However, if you stop and think about it for a moment, you'll notice that of the four boxes we have written "?/no", two of them cannot be false at the same time. In the first row, the red box checked whether "n1 > n2" while in the second row, the blue box checked whether "n2 > n1". They can't both be false at the same time. (Can they?) So we can actually be more certain that the table really should have the following values:
| > | n1 | n2 | n3 |
| max is n1 | ?/no | no | |
| max is n2 | ?/no | no | |
| max is n3 | yes | yes |
In that case, we go to line 7, where we check to see if n3 is bigger than the other two numbers. (Notice by this point of the program, the only plausible option is for both boxes in the table to be "yes".) Finally, the table looks like:
| > | n1 | n2 | n3 |
| max is n1 | ?/no | no | |
| max is n2 | ?/no | no | |
| max is n3 | yes | yes |
if( n1 > n2 && n1 > n3 ) max = n1; else if( n2 > n1 && n2 > n3 ) max = n2; else max = n3;Convince yourself that this works the same as Version 1 above.
Remember that there's always more than one way of writing a program. Here's another way.
1. if( n1 > n2 )
2. {
3. if( n1 > n3 )
4. max = n1;
5. else
6. max = n3;
7. }
8. else
9. {
10. if( n2 > n3 )
11. max = n2;
12. else
13. max = n3;
14. }
The great thing about this version is that it breaks up the conditions one by
one so that it's easier for us to walk through the logic of it.
Everything inside the curly braces is treated as a whole. So if line 1 is true, then lines 2-7 are associated with it, and if it is false, we go to line 8, in which case, lines 9-14 are associated with it. Let's look at the details further.
When line 1 is true:
| > | n1 | n2 | n3 |
| max is n1 | yes | ? | |
| max is n2 | no | ? | |
| max is n3 | ? | ? |
We write "yes" in the first row and we can infer that the opposite is false, so we write "no" in the second row. This means, either n1 or n3 can be the max at this point. So line 3 checks exactly this condition. Lines 4-6 are the results of that condition.
When line 1 is false:
| > | n1 | n2 | n3 |
| max is n1 | no | ? | |
| max is n2 | yes | ? | |
| max is n3 | ? | ? |
We write "yes" in the second row and we can infer that the opposite is false, so we write "no" in the first row. This means, either n2 or n3 can be the max at this point. So line 10 checks exactly this condition. Lines 11-13 are the results of that condition.
What if we had to find the largest number out of 100 numbers? Do you really want to enumerate all the possible comparisons? Imagine how big the table will be! That's 100 rows, and 100 columns! Which means, in the worst case, we make 100 x 100 comparisons. 10,000! There must be an easier way of finding the largest number. Afterall, finding max isn't conceptually that difficult.
What if we just keep track of the largest number as we check it against all the other numbers? This way, we don't have to check the numbers against each other. Instead, we check the numbers against a "max". Here's what we can try:
| > | n1 | n2 | n3 |
| max | ? | ? | ? |
Have a table that checks for "max" against each number once. In other words, we compare max against n1, compare max against n2, compare max against n3, and we're done. That's 3 comparisons in total, when we have 3 numbers. If we had 100 numbers, that's 100 comparisons. (That's 99900 comparisons less than before.) What would the corresponding Java program look like? Much simpler:
1. max = -999999; 2. if( n1 > max ) 3. max = n1; 4. if( n2 > max ) 5. max = n2; 6. if( n3 > max ) 7. max = n3;
We can start off by giving max some really really small number, so that we are guaranteed that something we have will be bigger than it. (We do this because we don't want to have "max = 0" at the beginning, but have a test case like n1 = -9, n2 = -11, n3 = -7. Then the program will never work, and 0 will always be the biggest number, even though 0 wasn't one of the original numbers we were given.) Whenever we encounter a number that is bigger than max, we just update the value in max. So, lines 2, 4, 6 make one check for each number we're given. Note that if any of these checks are false, nothing happens to max, so max will be the same as before. It is only when the checks are true, then max will get updated. By the end, we will have checked through all of the numbers, and max will have stored the largest number.
We can even skip the first step, and do the following:
1. max = n1; 2. if( n2 > max ) 3. max = n2; 4. if( n3 > max ) 5. max = n3;Even simpler and few lines!