Project 3 -- Three-Halves Problem

25 points

This is one of four major individual assignments that you are required to complete in this class. You MUST design and code this project on your own. You can ask for debugging help, particularly from the tutors, TA or instructor, but this project is to be your own work. The project relies on knowledge and skills that you will develop in Recitation Laboratories 1-8. If you are having trouble with these recitations after your in-class session, please be sure that you come to the lab at another time and sit down with a tutor.


Objectives:


Overview:

There is a famous mathematical property of integers, almost like a game one can play:
The Three-Halves Problem
  1. Start with any positive integer n, and carry out Step 2, a replacement step.

  2. if n is odd then replace n by (3n+1)/2
    else if n is even then replace n by n/2.

  3. Repeat Step 2 over and over again until a 1 appears.
    If no 1 appears, repeat Step 2 indefinitely.

For example, if we start with n=17, the game produces the following sequence of integers, called a run, of length 10:

In this case and in every other case that individuals have tried, the sequence reaches 1, and repeats 2, 1, 2, 1, ... indefinitely, so once 1 is reached, the sequence is regarded as at an end. In calculating the length, the initial number counts, and numbers count up to and including the first time 1 is reached. (Check that 1 is replaced by 2 and then by 1, so nothing more happens after one of these sequences gets to 1.) Researchers conjecture that the sequence converges to 1 for any starting positive integer n, but no one has ever been able to prove this. For this assignment, you are to write a program to investigate this game and to practice with features of Java. The work should proceed in three phases.


Phase I: Initial Software Development Strategy:

Initially you should implement the basic run from an initial integer to 1. To do this write a class ThreeHalvesRun with the following outline:

There should also be a ThreeHalvesTest class that reads an integer n, creates an instance of ThreeHalvesRun with input n, and uses the toString and an explicit call to runPrint to print the run. The output after reading 27 could look as follows:


Phase II: Finding the longest run for any integer up to a limit:

For this phase you should start in a new folder with new copies of the classes from Phase I. Your program should examine all runs for integers less than an initial value n. From among all these runs, the program should find the one with the longest run length (first occurrence).

You should modify your class ThreeHalvesRun as follows:

There should be a new class ThreeHalves with just a few things in it:

Finally, as before, there should also be a ThreeHalvesTest class, with a main method that reads an integer n, creates an instance of ThreeHalves with input parameter n. Then it should fetch the array created by ThreeHalves, and pass the array to a static method:

Notice that in this case the above static method would find the maximum element in any array that implements the Comparable interface.

The output after reading 28 should be the same as above, since it gives the integer less than 28 producing the longest run, and this will be 27. In fact all integers up to 55 keep giving 27 as the answer, but 55 gives 54 as an answer, since there is a run of length 72 starting with 54.

Entering 1000 should produce:


Phase III: Finding the maximum value in any run for any integer up to a limit:

For this phase it would probably be best to start in a new folder with new copies of everything, but if everything has been done correctly, all you need to is replace the compareTo method in the ThreeHalvesRun class, so that comparisons are based on the maximum value, rather than on the length of run. (Thus you could just carefully comment out the old compareTo method and put in a new one.)

Notice that with just the change of the single method in a single class, one is answering a completely different question:

This time entering any number less than 256 should produce the answer integer 27 with maximum length 4616. However, initial number 256 produces the result:

Here the run length is much shorter than for 27, but the maximum value is larger.

Notice also that there are other integers between 27 and 255 with maximum value 4616, but your software is supposed to produce the first (smallest) one.

If 1000 is entered, the result is:


Deliverables:


Questions:

  1. Can you think of an intuitive reason why the sequence eventually reaches 1?
  2. What is the "mysterious overflow number" described in the comments below?
  3. (Hard) What happens if odd integers n are replaced by (5n + 1)/2 instead of (3n + 1)/2 in the three-halves run? (You would really have to experiment in order to answer this.)


Other Comments:

For initial numbers less than or equal to 113 382, the largest value in any run is already attained using initial number 77 671, which gives length 149, but maximum value 785 412 368. Since this was obtained after dividing by 2, internally the largest value appearing is 1 570 824 736. Calculations involving 113 383 result in integer overflow for 32-bit integers, that is, larger than 231 - 1 = 2 147 483 647. In C, C++, or Java, one can use double type for calculations. This effectively gives exact integer arithmetic up to size 52 bits. In Java one can also use the long type, giving 64-bit integers. Finally, Java has the BigInteger class that provides integers of arbitrary size. You could try one of these methods to discover the mysterious overflow number in the run starting with 113 383.