Ruth's Recurrence Variation 2
// // Written by Grant Jenks // http://www.grantjenks.com/ // // DISCLAIMER // THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE // LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR // OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, // EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE // ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. // SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY // SERVICING, REPAIR OR CORRECTION. // // Copyright // This work is licensed under the // Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License. // To view a copy of this license, visit // http://creativecommons.org/licenses/by-nc-nd/3.0/ // or send a letter to Creative Commons, 171 Second Street, Suite 300, // San Francisco, California, 94105, US.A // // Description // This is a variation on "Ruth's Recurrence" described below. In this // variation, step (3) is modified such that the "1" or "2" is added to the // front of the number rather than the end. // // As I experimented with this variation I noticed that the number sequence // always repeated but not necessarily at the starting number. For instance, // if I begin with 1, the sequence is: // 1 21 7 27 9 3 *1* 21 7 27 9 3 *1* ... // so every 6 iterations, the sequence repeats. Now, looking at values through // 13, you'll see a similar pattern. But with 14, something changes: // 14 114 38 138 46 246 82 282 94 294 98 198 66 22 222 74 174 58 258 86 186 // 62 162 54 18 6 2 12 4 24 8 *18* 6 2 12 4 24 8 *18* ... // Did you notice it? The sequence will repeat forever but it never returns to // 14. Instead, it repeats at 18. This property yields two notions: // 1. There exists a 'distance to repetition'. That is, the number of // iterations that must be performed before a number is repeated. // 2. There exists a 'period'. That is, the number of iterations after // observing a repetition before observing that number again. // // Description of Ruth's Recurrence: // My in-laws visited on a recent weekend. During their stay with us, Shannon's // Mom said she had created a kind of math-puzzle to keep herself busy. Curious, // she described the following algorithm: // 1. Start with an integer greater than 0. // 2. If the number is divisible by 3, divide by 3. // 3. Else, add a "1" or "2" to the end of it to make it divisible by 3. // 4. If the current number is now 1, end. Else, go back to (2). // She also shared that she had started with the number 141 and after a dozen // or so iterations suspected that it would not reach 1. // // (Arithmetic-savvy readers will notice that step 3 works on a clever trick: // if you sum the digits in an integer and the sum is divisible by 3 then the // integer is divisible by 3.) // // Notes // * Depends on .NET 4.0. // * Compile with /R:System.Numerics.dll // * To enable tracing, compile with /d:TRACE // using System; using System.Numerics; using System.Diagnostics; using System.Collections.Generic; class RuthsRecurrence { const int IterationLimit = 1000000; const int ValueCountLimit = 500; static readonly BigInteger Ten = 10; static readonly BigInteger Three = 3; // // Sum and count the digits in the BigInteger. // // Note that this function accumulates the sum in an int. This limits the // BigInteger to something on the order of 100,000,000 digits. // // Assumes the value is positive. // static int SumAndCountDigits(BigInteger value, out int count) { int sum = 0; for (count = 0; value > BigInteger.Zero; count++) { BigInteger remainder; value = BigInteger.DivRem(value, Ten, out remainder); sum += (int)remainder; } return sum; } // // Do the recurrence for the given BigInteger. // static bool Recur(BigInteger start, out int distance, out int period) { int iteration = 0; BigInteger current = start; HashSet<BigInteger> previous = new HashSet<BigInteger>(); bool foundRepeat = false; distance = 0; period = 0; Trace.WriteLine(String.Format("Start: {0}", start)); while (iteration < IterationLimit) { Trace.WriteLine(String.Format( "Iteration: {0}, Distance: {1}, Period: {2}, Current: {3}", iteration, distance, period, current)); if (previous.Contains(current)) { if (!foundRepeat) { // Found the first repeated value in the sequence. 'distance' // counts the number of steps to this point. Now we measure the // period at which repetition occurs. foundRepeat = true; previous.Clear(); } else { // The sequence repeated after we had already found a repetition. // This means our 'period' value is computed. We're done. break; } } previous.Add(current); BigInteger remainder; BigInteger quotient = BigInteger.DivRem(current, Three, out remainder); if (remainder == BigInteger.Zero) { current = quotient; } else { int cnt; int sum = SumAndCountDigits(current, out cnt); int rem = sum % 3; int inc = 3 - rem; current = (inc * BigInteger.Pow(10, cnt)) + current; } if (!foundRepeat) { distance++; } else { period++; } } return (iteration < IterationLimit); } // // Iteratively test values for convergence and print result. // // Program entry point. Arguments are ignored. // static int Main(string[] args) { Trace.AutoFlush = true; Trace.Listeners.Add(new ConsoleTraceListener(true)); BigInteger value = BigInteger.One; for (int i = 0; i < ValueCountLimit; i++) { int distance, period; if (Recur(value, out distance, out period)) { Console.WriteLine( "Value {0} repeats in {1} iterations with period {2}", value, distance, period); } else { Console.WriteLine("Value {0} failed to repeat within {1} iterations", value, IterationLimit); } value += 1; } return 0; } }
Here's a plot of the 'distance' and 'period' for values 1 through 366. The period is generally very short and looks nearly regular (but it isn't quite) and the distance appears random. Viewing it from 20-feet back, this recurrence has less structure in the “iterations to repetition” (aka distance) but more structure in the period than the original Ruth's Recurrence.