Computer Science 2311
Fall 2009
Laboratory Assignment 2

Handed out: 9/1/09


You should find this a challenging assignment. Follow the instructions. Ask questions when you have trouble and do not see what to do. Do not just stare at the computer hoping for inspiration.


Greatest common divisors

All of the numbers for this assignment are nonnegative integers.

The greatest common divisor (also called the greatest common factor) of two numbers n and m, written gcd(m, n), is the largest number k such that k is a divisor of both m and n. For example,

Notice that 1 is a divisor of every number, so there is always at least one common divisor to choose from.


An algorithm to compute greatest common divisors

One way to find the greatest common divisor of m and n is to try each possible number, starting at the smaller of m and n and counting down until a divisor of both is found. Here is a Java program fragment to set g = gcd(m, n). It assumes that m and n have already been given values. It uses type long for integers, which is similar to int, but allows larger integers. Recall that x % y is the remainder when you divide integer x by integer y. So x % y is 0 if x is divisible by y.

    long g,m,n;

    ... (give m and n values here)

    //-------------------------------------------
    // Make g be the smaller of m and n to start.
    //-------------------------------------------

    if(m < n) g = m;
    else g = n;

    //-------------------------------------------------
    // Decrease g until g is a divisor of both m and n.
    // This loop will have to stop, since 1 is a divisor
    // of both m and n.
    //-------------------------------------------------

    while(n % g != 0 || m % g != 0) 
    {
      g = g - 1;
    }


Getting m and n from the user

We have not discussed how to get information from the user. Unfortunately, it is not a simple thing to do in Java, and it employs some advanced ideas. Here is a program fragment that will read two integers (of type long) from the keyboard and store them into m and n. For now, we will just use this without trying to understand what it is saying. Later, we will look at it in more detail. First, you will need to write

    import java.io.*;
in your file, before the class starts, and you will need to add throws Exception after the line that introduces the main method. (See the program below.) Then, when you need to read the numbers, do the following.
    InputStreamReader   reader = new InputStreamReader(System.in);
    BufferedReader    keyboard = new BufferedReader(reader);

    System.out.println("Compute the gcd of what two numbers?");
    System.out.print(" m = ");
    m = Long.parseLong(keyboard.readLine());
    System.out.print(" n = ");
    n = Long.parseLong(keyboard.readLine());
Notice that it writes a request for the numbers, and reads each one.


Building a full Java program

You create a Java program by defining a class (a group of related things) and putting a main method in the class. Here is a complete program that asks the user for two integers and shows their greatest common divisor.

// Your name here
// Computer science 2311
// Lab assignment 2A
// Date here

// This program reads two numbers from the user and
// prints their greatest common divisor.

import java.io.*;

public class Program2a
{
  public static void main(String[] args) throws Exception
  {
    long g,m,n;

    InputStreamReader   reader = new InputStreamReader(System.in);
    BufferedReader    keyboard = new BufferedReader(reader);

    System.out.println("Compute the gcd of what two numbers?");
    System.out.print(" m = ");
    m = Long.parseLong(keyboard.readLine());
    System.out.print(" n = ");
    n = Long.parseLong(keyboard.readLine());

    //-------------------------------------------
    // Make g be the smaller of m and n to start.
    //-------------------------------------------

    if(m < n) g = m;
    else g = n;

    //-------------------------------------------------
    // Decrease g until g is a divisor of both m and n.
    // This loop will have to stop, since 1 is a divisor
    // of both m and n.
    //-------------------------------------------------

    while(n % g != 0 || m % g != 0) 
    {
      g = g - 1;
    }

    System.out.println("The gcd of " + m + " and " + n + " is " + g);
  }
}

Create a project. In your project, create a class called Program2a, and put the above program into it. Do not type it yourself. Just copy it from your browser and paste it into your program.

Try your program on small numbers. Does it seem to work?

Now try it on two 9 digit numbers. For example, try to get gcd(123456789, 987654321). Does it still work? Time it, and record how long it takes. (You can display a clock by double-clicking the clock at the lower-right of the screen.)

Now try it on two 10 digit numbers. For example try to get gcd(1234567890, 9123456789). Does it work? How long does it take?

How long do you think it will take to find the greatest common divisor of two 11 digit numbers?

If m is the smaller of the two numbers, then the loop might go through m times. So if m is about one million, it might need to go around one million times.


An efficient algorithm to compute greatest common divisors

The Greek mathematician Euclid discovered a simple and efficient way to find the greatest common divisor of two numbers. It is based on the following facts.

(1)   gcd(0, n) = n
(2)   gcd(m, n) = gcd(n % m, m) (when m > 0)
An algorithm starts with two variables i and j set to m and n, respectively. At each stage, it replaces i and j by j % i and i, respectively, using the fact
gcd(i, j) = gcd(j % i, i) (when i > 0)
That way, even as i and j change, gcd(i, j) is always the same. Here is a sample run for m = 10 and n = 36. Each line shows the values of i and j when you reach the top of the loop.
                i               j
              _____           _____
               10               36
                6               10
                4                6
                2                4
                0                2
Notice that gcd(10, 36) = gcd(6, 10) = gcd(4, 6) = gcd(2, 4) = gcd(0, 2) = 2. So, as i and j change, gcd(i, j) is always the same as gcd(m, n), even though m and n do not change. The loop stops when i = 0, with the answer being j.

Question: We tried Euclid's algorithm for m = 10 and n = 36. Does Euclid's algorithm work for the case of m = 36 and n = 10? Try it. The value of 10 % 36 is 10. Does it work?


Another program

Create another project, with a class called Program2b. Copy what is in Program2a.java into Program2b.java. Leave the part that gets m and n from the user, and the part that writes out the answer. But delete the part that computes the greatest common divisor.

Remove variable g, and add two new variables i and j. Make them have type long.

Now make the program compute the greatest common divisor of m and n using Euclid's algorithm. Before starting the loop, make i = m and j = n. Then keep going as long as i > 0. How should you update i and j?

Test your program? Does it give correct answers? Whether or not it does, move on to the next section.


Turning on the lights

Good programmers do not just presume that their programs work. They turn on the lights to see what is happening. Make your program write out the values of i and j each time around the loop. Just inside the loop, before modifying i and j, add the following.

  System.out.println("Loop: i = " + i + " j = " + j);
This is called a trace print. Now run your program. Try it on 10 and 36. Is the trace information what you expect?

If the trace is not what you expect, do a hand simulation to see why. Just run your loop carefully by hand. Can you duplicate what you are getting when you run the program? Can you see how to fix the problem? Feel free to create additional variables.


Is it really more efficient?

After you get Program2b.java working, comment out the trace print by writing // in front of it. Do not delete it entirely. Now try the program on numbers of 9, 10 and 11 digits. Does it produce the answer quickly?


Turning in the assignment

Print both versions, Program2a.java and Program2b.java. Be sure that your name is part of both of them, in the comment at the top. The comment in Program2a.java should say that it is lab assignment 2A, and the comment in Program2b.java should say that it is lab assignment 2B. Turn in both.