Programmatic Mathematica XVII: The Collatz Conjecture

There has been a lot of interest recently in the Collatz Conjecture. A lot of video blogs are going into it, particularly Numberphile, a vlog present on YouTube. It might have something to do with the fact that this year is the 70th anniversary of the conjecture. It is a simple idea, easy enough for a child to understand. Yet, it has been difficult enough that no one has been able to either prove or disprove it to this day.

The Collatz Conjecture is the hunch, or guess, or idea, that performing a certain recursive operation on any positive integer leads to the inevitable result that repeated operations on all successors will lead to the number 1. After that, the sequence of {1, 4, 2, …} occurs in an infinite repetition.

This problem was first posed by Lothar Collatz in 1937. The reason it is only a conjecture is that no one has been able to prove it for all positive integers. It is only conjectured to work as such. Over the past seventy years, no one has been able to furnish a counterexample where the number 1 is not reached. So by now, we’re “pretty sure” Collatz is correct for all positive integers.

I thought of some Mathematica code to write for this. The algorithm would go something like:

  1. Precondition: n > 0; n \in Z
  2. If n is 1, return 1 and exit
  3. If n is even, return n/2
  4. If n is odd, return 3n + 1
  5. Go back to line 2.

Like Fermat’s Last Theorem, which has been proved once and for all in 1995 by Professor Andrew Wiles, and aided by Richard Taylor, the Collatz Conjecture is simple enough to describe to any lay person (as I just did), but its proof has eluded us.

The application of the above algorithm to Mathematica code involves some new syntax. Sow[n] acts as a kind of array for anyone who doesn’t want to declare and implement an array. I would suppose that the programmers of the Mathematica language didn’t see the need for an array for many implementations, such as sequences of numbers. If you want to generate a sequence, you want the numbers in order from some lower bound, up to some upper bound. If you want to list them, you want to do the same thing. It is not often that you want to access only one particular value inside the sequence. This is for those people who just want the whole sequence uninterrupted.

I guess what Sow[n] does is leave the members of the sequence lying around in some pre-defined region in computer memory. That memory is likely to be freed once the Reap[n] function is called, which lists all the members of the stored sequence in the order generated.

EvenQ[] and OddQ[] are employed to check if n if odd or even before executing the rest of the line. If false, control passes through the next line. The testing is inefficient here, since each statement is tested all the time. So, if we already know the number is even, OddQ[] is executed anyway.

ClearAll[Co];
Co[1] = 1;
Co[n_ /; EvenQ[n]] := (Sow[n]; Co[n/2])
Co[n_ /; OddQ[n]] := (Sow[n]; Co[3*n + 1])
Collatz[n_] := Reap[Co[n]]

But Reap[n] by itself gives a nested array (or more accurately, a “ragged” array) with the final “1” outside of the innermost nesting, where the other numbers are.

In[10]:= Collatz[7]
Out[10]= {1, {{7, 22, 11, 34, 17, 52, 26, 13,
    40, 20, 10, 5, 16, 8, 4, 2}}}

Nested arrays are un-necessary, but the remedy to this gets rid of the number “1” which is the number the Collatz function is supposed to always land on. So we then rely on the presence of the number “2”, the number arrived at before going to “1”, at the end of the sequence. Getting rid of the nested array relies on using Flatten[Reap[Co[n]]]. But when you do that, this happens:

In[11]:= Collatz[7]
Out[11]= {1, 7, 22, 11, 34, 17, 52, 26, 13,
    40, 20, 10, 5, 16, 8, 4, 2}

Flattening has the effect of placing the ending 1 at the beginning of the array. If we can live with this minor inconvenience, then we are able to test the Collatz Conjecture on wide ranges of positive integers. So, this is the code we ended up with:

ClearAll[Co];
Co[1] = 1;
Co[n_ /; EvenQ[n]] := (Sow[n]; Co[n/2])
Co[n_ /; OddQ[n]] := (Sow[n]; Co[3*n + 1])
Collatz[n_] := Flatten[Reap[Co[n]]]

The sequences generated by the Collatz conjecture have the well-documented property of having common endings. Using the Table[] command, we can observe the uncanny phenomena that most of these sequences end in “8, 4, 2” (or, to be more precise, “8, 4, 2, 1”). Here are the sequences generated for the numbers from 1 to 10:

In[38]:= Table[Collatz[i], {i, 10}]

Out[38]= {{1}, 
          {1, 2}, 
          {1, 3, 10, 5, 16, 8, 4, 2}, 
          {1, 4, 2}, 
          {1, 5, 16, 8, 4, 2}, 
          {1, 6, 3, 10, 5, 16, 8, 4, 2}, 
          {1, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2},
          {1, 8, 4, 2}, 
{1, 9, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2},    
          {1, 10, 5, 16, 8, 4, 2}}

Because even numbers are to be divided by 2, somewhere along the meanderings of the sequence, a power of 2 is encountered, and from there it’s a one-way trip to the number “1”.