-2

This should be fairly basic. I feel like i'm missing something glaringly obvious but I've been staring at this for a while now. I've got a recursive method creating a Collatz sequence (using as recursion practice) However once my method reaches the requirements for exit I have a "return value" which then jumps back to the recursive call within the method. If anyone can tell me what i'm missing i'd be very appreciative! Thank you, code below!

public int sequenceCreator(int currentVal, int numOfSteps)
    {
        int nextVal;
        while (currentVal != 1)
        { 
            if (currentVal % 2 == 0)
            {
                nextVal = currentVal / 2;
                numOfSteps++;
            }
            else // (currentVal % 2 > 0)
            {
                nextVal = (currentVal * 3) + 1;
                numOfSteps++;
            }
            return sequenceCreator(nextVal, numOfSteps);
        }

        return numOfSteps;
    }
Thomas Fox
  • 521
  • 2
  • 5
  • 16
  • What is the question? The method seems to return just fine. – Lasse V. Karlsen Sep 10 '18 at 14:02
  • 1
    You are doing recursion wrong. Get rid of the while loop. – Fildor Sep 10 '18 at 14:02
  • 2
    Why have a `while` statement that never executes more than once? – Servy Sep 10 '18 at 14:03
  • 2
    "once my method reaches the requirements for exit I have a "return value" which then jumps back to the recursive call within the method." - That's how recursion works. You build up a "stack" and as soon as your exit criterion is met, it will return to the caller, which will return to it's caller ... until the initial caller is reached. – Fildor Sep 10 '18 at 14:09
  • 1
    Once an instance of sequenceCreator calls itself again when that inner one returns it will return to the original call at the point the recursion happened, ie where you called the recursive method. This sounds like what you are seeing and is exactly what we expect to happen. Can you clarify what exactly the problem you are seeing is? ie are you getting an error, incorrect result, etc.? – Chris Sep 10 '18 at 14:10
  • Do you **have** a problem? Can you post some testcases that produce a result and post what you got, and what you really wanted to get? It seems to me that your recursive approach works for the testcases I trid. Granted, due to the recursion, it will exhaust the stack for large `currentVal` values, but other than that, what exactly is wrong with it? – Lasse V. Karlsen Sep 10 '18 at 14:13
  • Right yeah that makes sense. It's jumping between the return call and the method call once the method is complete. I'll add the main method in as well. Should've done that in the first place hold on – Thomas Fox Sep 10 '18 at 14:15
  • If that is actually your question you should just find a tutorial that shows how a recursive Fibonacci sequence implementation works, as that is the easiest to deal with and explain, it should explain the part about the stack and so on just fine, like this one: [Youtube: Khan Academy: Stepping Through Recursive Fibonacci Function](https://www.youtube.com/watch?v=zg-ddPbzcKM) – Lasse V. Karlsen Sep 10 '18 at 14:16
  • Alright thankyou very much. Sorry if my question has been a bit unclear – Thomas Fox Sep 10 '18 at 14:17
  • 1
    @ThomasFox I don't think the confusion is your fault. You did not really understand recursion, which is crucial to understanding recursion ;D – Fildor Sep 10 '18 at 14:18
  • 1
    Yeah, recursion will throw you for a loop... oh, wait... – Lasse V. Karlsen Sep 10 '18 at 14:22
  • Hah. Thanks. Nice touch with the joke! Thanks again guys! – Thomas Fox Sep 10 '18 at 14:23

3 Answers3

0

return only exits that specific method call. It doesn't abort all of the times sequenceCreator() was called.

In this case, that should be okay, because it returns to this line:

return sequenceCreator(nextVal, numOfSteps);

which will in turn return again to it's caller, and so on, until you finally resolve all of the recursive calls.

But I might write the method like this, instead:

public int sequenceCreator(int currentVal)
{
    if (currentVal == 1) return 1;

    if (currentVal % 2 == 0)
    {
        return 1 + sequenceCreator(currentVal / 2);
    }
    else // (currentVal % 2 > 0)
    {
        return 1 + sequenceCreator(currentVal * 3 + 1);
    }
}

That code produces the same result for the same input, but with less code that's easier to understand, and without needing to pass extra state between method calls.

For fun, we can reduce the code further with a ternary operator (but I don't recommend using this version, as imo readability suffers):

public int sequenceCreator(int currentVal)
{
    if (currentVal == 1) return 1;
    return 1 + sequenceCreatetor(currentValue % 2 == 0? currentVal / 2 : currentVal * 3 + 1);
}

I show this version to illustrate why recursion is used. Recursive problems are, at their hearts, stack problems. For every recursive solution, there exists a matching solution that relies only on traditional methods, using a stack instead (the recursive solution merely "hides" the stack, by relying on the program's call stack). However, for certain types of problem, recursion allows for a drastic reduction in the amount of code needed to solve it. And so we have also the inverse; if you find yourself using a stack, there might be a way to use recursion to greatly simplify the problem. In this case, the method body is only two lines, and if I really wanted to I could get it down to a single line of code.

You also need to understand this does not create a sequence. It creates exactly one value, in cases where it converges at all. If you actually want to create a sequence, you need to return an IEnumerable, ideally using the yield keyword:

public IEnumerable<int> sequenceCreator(int currentVal)
{
    if (currentVal == 1) yield return 1;

    if (currentVal % 2 == 0)
    {
        yield return 1 + sequenceCreator(currentVal / 2);
    }
    else // (currentVal % 2 > 0)
    {
        yield return 1 + sequenceCreator(currentVal * 3 + 1);
    }
}

Logically, I think you're safe here, and it will converge. The "final" or base case of this method is an input of 1. Calls where the input is even reduce towards that base case. Calls where the input is odd increase away from the base case, but in such a way where next input is always even, and always a different even value than we've tried previously. Eventually we hope to end up with a power of two, or 3 time a power of two, that will remain even as it reduces all the way down to 3 or 2, and then 1, and then exit.

However, I'm concerned there might exist some values that never reach this state, or overflow integer before they can, or after increasing a few times reduce back to an even value we've already tried, thus creating a never-ending cycle.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • Yes I understand that. i'm only trying to return the number of values in the sequence with a specific start value. – Thomas Fox Sep 10 '18 at 14:20
-1

I think there should not be any loop. Use if condition instead of while.

If (currentVal != 1)

Code OverFlow
  • 913
  • 1
  • 13
  • 28
-2

Avoid recursion if possible:

        int sequenceNumber = Convert.ToInt32(Console.ReadLine());
        List<int> list = new List<int>();

        while (sequenceNumber>=1)
        {
            if (sequenceNumber == 1)
            {
                Console.WriteLine(1);
                sequenceNumber = Convert.ToInt32(Console.ReadLine());
            }

            else if(sequenceNumber>1)
            {
                while (sequenceNumber>=1)
                {
                    if (sequenceNumber == 1)
                    {
                        list.Add(sequenceNumber);
                    }

                    else if (sequenceNumber % 2 == 0)
                    {
                        list.Add(sequenceNumber);
                        sequenceNumber = sequenceNumber / 2;

                    }
                    else if (sequenceNumber % 2 != 0)
                    {
                        list.Add(sequenceNumber);
                        sequenceNumber = sequenceNumber * 3 + 1;

                    }
                }

                list.ForEach(Console.WriteLine);
                foreach (int i in list)
                {
                    Console.Write(i + " ");

                }
            }

            sequenceNumber = Convert.ToInt32(Console.ReadLine());
        } 
    }
Adam Ostrožlík
  • 1,256
  • 1
  • 10
  • 16