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.

random/ruth_s_recurrence_variation_2.txt · Last modified: 2011/07/07 02:45 by grant