Start learning free

LESSON 139 OF 415 · LOOPS & CONDITIONALS

Collatz Conjecture in Swift: Printing the Sequence with a while Loop

Practice Read the full explanation

The Collatz conjecture in mathematics asks whether repeating two simple arithmetic operations will eventually transform every positive integer into one. It concerns sequences of integers in which each term is obtained from the previous term as follows:
if the previous term is even, the next term is one half of the previous term.
If the previous term is odd, the next term is 3 times the previous term plus 1.
The conjecture is that these sequences always reach 1, no matter which positive integer is chosen to start the sequence.

You are given an integer n and you should print to the console all the values that n takes throughout the Collatz Conjecture.

Example:

Input: n = 3

Output:
3
10
5
16
8
4
2
1

Explanation:
n = 3  => n is odd  => n becomes 3 * 3 + 1 = 10
n = 10 => n is even => n becomes 10 / 2    = 5
n = 5  => n is odd  => n becomes 5 * 3 + 1 = 16
n = 16 => n is even => n becomes 16 / 2    = 8
n = 8  => n is even => n becomes 8 / 2     = 4
n = 4  => n is even => n becomes 4 / 2     = 2
n = 2  => n is even => n becomes 2 / 2     => 1
n = 1 => stop
Hints:

Look at the example and notice that we apply a transformation over and over until we reach a final point where we stop. Does that ring a bell?

We need to repeat the transformation code as long as one condition holds (n is greater than 1). How do you write that in code?

We will use a loop that executes as long as n > 1. What should we do inside this loop?

If n is even, we should apply a transformation. Otherwise, we should apply the other transformation. How do we handle these different scenarios?

We can do this using an if - else statement.

Video Solution:

STEP 1 OF 6
    Ctrl + Enter to check

    SWIFT · LOOPS & CONDITIONALS

    Collatz Conjecture in Swift, explained

    Problem overview

    The method printSeries takes an integer n and prints every value of the Collatz sequence that starts at n. Each term comes from the previous one: if it is even, the next term is half of it; if it is odd, the next term is three times it plus one. The sequence stops when it reaches 1, which the conjecture says always happens for positive starting values.

    For n = 3 the output is 3, 10, 5, 16, 8, 4, 2, 1, one number per line. The starting value is printed too, and so is the final 1.

    Approach: repeat until the value reaches 1

    The number of steps isn't known in advance, so a while loop fits better than a for loop. The loop keeps going as long as the current value is greater than 1, and each pass applies one of the two rules and prints the result. The starting value is printed before the loop, since the loop prints only the values it produces.

    The current value changes, so it must live in a variable. Swift's function parameters are constants, so n itself can't be updated, and the method copies it into a variable first.

    Tracing n = 6

    The method prints 6 first. The loop sees 6 > 1, 6 is even, so num becomes 3 and 3 is printed. Next, 3 is odd, so num becomes 10 and 10 is printed. Then come 5, 16, 8, 4 and 2. When num becomes 1 it is printed, and the loop condition 1 > 1 is false, so the method ends. The 1 is printed because the print comes right after the update, before the condition is checked again.

    Step by step

    1. Copy the parameter into a variable. var num = n creates a changeable value. With let, or by assigning to n directly, the compiler reports that the value is a constant.
    2. Print the starting value. It is the first number of the sequence.
    3. Write while num > 1. Test num, not n: only num changes in the loop body, so a condition on n would never change.
    4. Handle the even case. If num % 2 == 0, set num = num / 2. Dividing two Int values drops any remainder, and for an even number there is none.
    5. Handle the odd case in the else branch: num = 3 * num + 1.
    6. Print num after the if/else, still inside the loop, so every new term is printed whichever rule produced it.

    The solution in Swift

    class Solution {
        func printSeries(_ n: Int) {
            var num = n
            print(num)
            while num > 1 {
                if num % 2 == 0 {
                    num = num / 2
                } else {
                    num = 3 * num + 1
                }
                print(num)
            }
        }
    }
    

    Swift details worth knowing:

    • var versus let. var declares something you can reassign. Parameters behave like let, which is why the copy is needed.
    • Integer division. / between Int values gives an Int and truncates toward zero: 7 / 2 is 3. It is exact here only because the branch handles even numbers.
    • No parentheses around conditions, but the braces are mandatory.
    • Overflow traps. Int is 64 bits, and arithmetic that overflows crashes the program instead of wrapping. Sequences from ordinary starting values stay far from that limit: starting at 27, the sequence takes 111 steps and peaks at 9232.

    Why the output can look surprising

    The sequence doesn't shrink steadily. Small starting values can climb well above where they began before falling: 3 reaches 16, and 27 climbs to 9232 before it finally comes down to 1. Printing every term shows that rise and fall, and it is why the loop must test the current value rather than count down from n.

    Complexity

    The running time depends on the length of the sequence, and nobody has proved a bound for it, since the conjecture itself is unproven. For any starting value you can actually try, the sequence ends, and the loop does a constant amount of work per term. The space is O(1): a single variable.

    Edge cases and common mistakes

    • Starting at 1. The method prints 1 and the loop doesn't run.
    • Starting at 0 or below. The condition num > 1 is false immediately, so only the starting value is printed. 0 and negative numbers are outside the problem, and the loop is not entered for them.
    • Printing the start twice or not at all. The start is printed before the loop, and only new terms inside it.
    • Printing inside only one branch. The final print(num) must come after the else block, or half the terms are missing.
    • A loop condition on n. It would never become false, so the program would never stop.

    Testing

    • printSeries(3) prints 3, 10, 5, 16, 8, 4, 2, 1.
    • printSeries(6) prints 6, 3, 10, 5, 16, 8, 4, 2, 1.
    • printSeries(1) prints 1.
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free