Question

I'm having a problem generating the Terras number sequence.

Terras formula

Here is my unsuccessful attempt:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Terras
{
    class Program
    {
        public static int Terras(int n)
        {
            if (n <= 1)
            {
                int return_value = 1;
                Console.WriteLine("Terras generated : " + return_value);

                return return_value;
            }
            else
            {
                if ((n % 2) == 0)
                {
                    // Even number
                    int return_value = 1 / 2 * Terras(n - 1);
                    Console.WriteLine("Terras generated : " + return_value);

                    return return_value;
                }
                else
                {
                    // Odd number
                    int return_value = 1 / 2 * (3 * Terras(n - 1) + 1);
                    Console.WriteLine("Terras generated : " + return_value);

                    return return_value;
                }
            }
        }
        static void Main(string[] args)
        {
            Console.WriteLine("TERRAS1");
            Terras(1); // should generate 1

            Console.WriteLine("TERRAS2");
            Terras(2); // should generate 2 1 ... instead of 1 and 0

            Console.WriteLine("TERRAS5");
            Terras(5); // should generate 5,8,4,2,1 not 1 0 0 0 0

            Console.Read();
        }
    }
}

What am I doing wrong?

I know the basics of recursion, but I don’t understand why this doesn’t work.

I observe that the first number of the sequence is actually the number that you pass in, and subsequent numbers are zero.

Was it helpful?

Solution

Change 1 / 2 * Terros(n - 1); to Terros(n - 1)/2;

Also 1 / 2 * (3 * Terros(n - 1) + 1); to (3 * Terros(n - 1) + 1)/2;

1/2 * ... is simply 0 * ... with int math.


[Edit]

Recursion is wrong and formula is mis-guided. Simple iterate

public static void Terros(int n) {
  Console.Write("Terros generated :");
  int t = n;
  Console.Write(" " + t);
  while (t > 1) {
    int t_previous = t;
    if (t_previous%2 == 0) {
      t = t_previous/2;
    }
    else {
      t = (3*t_previous+1)/2;
    }
    Console.Write(", " + t);
  }
  Console.WriteLine("");
}

The "n is even" should be "t(subscript n-1) is even" - same for "n is odd".

OTHER TIPS

  int return_value = 1 / 2 * Terros(n - 1);
  int return_value = 1 / 2 * (3 * Terros(n - 1) + 1);

Unfortunately you've hit a common mistake people make with ints.

(int)1 / (int)2 will always be 0.

Since 1/2 is an integer divison it's always 0; in order to correct the math, just swap the terms: not 1/2*n but n/2; instead of 1/2* (3 * n + 1) put (3 * n + 1) / 2.

Another issue: do not put computation (Terros) and output (Console.WriteLine) in the same function

public static String TerrosSequence(int n) {
  StringBuilder Sb = new StringBuilder();

  // Again: dynamic programming is far better here than recursion
  while (n > 1) {
    if (Sb.Length > 0)
      Sb.Append(",");

    Sb.Append(n);

    n = (n % 2 == 0) ? n / 2 : (3 * n + 1) / 2;
  }

  if (Sb.Length > 0)
    Sb.Append(",");

  Sb.Append(n);

  return Sb.ToString();
}


// Output: "Terros generated : 5,8,4,2,1"
Console.WriteLine("Terros generated : " + TerrosSequence(5));

The existing answers guide you in the correct direction, but there is no ultimate one. I thought that summing up and adding detail would help you and future visitors.

The problem name

The original name of this question was “Conjuncture of Terros”. First, it is conjecture, second, the modification to the original Collatz sequence you used comes from Riho Terras* (not Terros!) who proved the Terras Theorem saying that for almost all t₀ holds that ∃n ∈ ℕ: tₙ < t₀. You can read more about it on MathWorld and chux’s question on Math.SE.

* While searching for who is that R. Terras mentioned on MathWorld, I found not only the record on Geni.com, but also probable author of that record, his niece Astrid Terras, and her family’s genealogy. Just for the really curious ones. ☺

The formula

You got the formula wrong in your question. As the table of sequences for different t₀ shows, you should be testing for parity of tₙ₋₁ instead of n.

t_n={1/2t_(n-1)   for t_(n-1) even; 1/2(3t_(n-1)+1)   for t_(n-1) odd

Formula taken from MathWorld.

Also the second table column heading is wrong, it should read t₀, t₁, t₂, … as t₀ is listed too.

You repeat the mistake with testing n instead of tₙ₋₁ in your code, too. If output of your program is precisely specified (e.g. when checked by an automatic judge), think once more whether you should output t₀ or not.

Integer vs float arithmetic

When making an operation with two integers, you get an integer. If a float is involved, the result is float. In both branches of your condition, you compute an expression of this form:

1 / 2 * …

1 and 2 are integers, therefore the division is integer division. Integer division always rounds down, so the expression is in fact

0 * …

which is (almost*) always zero. Mystery solved. But how to fix it?

Instead of multiplying by one half, you can divide by two. In even branch, division by 2 gives no remainder. In odd branch, tₙ₋₁ is odd, so 3 · tₙ₋₁ is odd too. Odd plus 1 is even, so division by two always produces remainder equal to zero in both branches. Integer division is enough, the result is precise.

Also, you could use float division, just replace 1 with 1.0. But this will probably not give correct results. You see, all members of the sequence are integers and you’re getting float results! So rounding with Math.Round() and casting to integer? Nah… If you can, always evade using floats. There are very few use cases for them, I think, most having something to do with graphics or numerical algorithms. Most of the time you don’t really need them and they just introduce round-off errors.

* Zero times whatever could produce NaN too, but let’s ignore the possibility of “whatever” being from special float values. I’m just pedantic.

Recursive solution

Apart from the problems mentioned above, your whole recursive approach is flawed. Obviously you intended Terras(n) to be tₙ. That’s not utterly bad. But then you forgot that you supply t₀ and search for n instead of the other way round.

To fix your approach, you would need to set up a “global” variable int t0 that would be set to given t₀ and returned from Terras(0). Then Terras(n) would really return tₙ. But you wouldn’t still know the value of n when the sequence stops. You could only repeat for bigger and bigger n, ruining time complexity.

Wait. What about caching the results of intermediate Terras() calls in an ArrayList<int> t? t[i] will contain result for Terras(i) or zero if not initialized. At the top of Terras() you would add if (n < t.Count() && t[n] != 0) return t[n]; for returning the value immediately if cached and not repeating the computation. Otherwise the computation is really made and just before returning, the result is cached:

if (n < t.Count()) {
    t[n] = return_value;
} else {
    for (int i = t.Count(); i < n; i++) {
        t.Add(0);
    }
    t.Add(return_value);
}

Still not good enough. Time complexity saved, but having the ArrayList increases space complexity. Try tracing (preferably manually, pencil & paper) the computation for t0 = 3; t.Add(t0);. You don’t know the final n beforehand, so you must go from 1 up, till Terras(n) returns 1.

Noticed anything? First, each time you increment n and make a new Terras() call, you add the computed value at the end of cache (t). Second, you’re always looking just one item back. You’re computing the whole sequence from the bottom up and you don’t need that big stupid ArrayList but always just its last item!

Iterative solution

OK, let’s forget that complicated recursive solution trying to follow the top-down definition and move to the bottom-up approach that popped up from gradual improvement of the original solution. Recursion is not needed anymore, it just clutters the whole thing and slows it down.

End of sequence is still found by incrementing n and computing tₙ, halting when tₙ = 1. Variable t stores tₙ, t_previous stores previous tₙ (now tₙ₋₁). The rest should be obvious.

public static void Terras(int t) {
    Console.Write("Terras generated:");
    Console.Write(" " + t);
    while (t > 1) {
        int t_previous = t;
        if (t_previous % 2 == 0) {
            t = t_previous / 2;
        } else {
            t = (3 * t_previous + 1) / 2;
        }
        Console.Write(", " + t);
    }
    Console.WriteLine("");
}

Variable names taken from chux’s answer, just for the sake of comparability.

This can be deemed a primitive instance of technique. The evolution of this solution is common to the whole class of such problems. Slow recursion, call result caching, dynamic “bottom-up” approach. When you are more experienced with dynamic programming, you’ll start seeing it directly even in more complicated problems, not even thinking about recursion.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top