Problem overview
The Collatz sequence starts from a positive integer n and repeats one rule until it reaches 1. If the current term is even, the next term is half of it. If the current term is odd, the next term is three times it plus one. The Collatz conjecture says that every positive starting value eventually reaches 1. Nobody has proved it, but it has held for every number that has been tried.
The method printSeries takes an Int named n and prints every value the sequence takes, starting with n itself and ending with 1. For n = 3 it prints:
3
10
5
16
8
4
2
1
3 is odd, so the next term is 3 * 3 + 1 = 10. 10 is even, so it becomes 5, and so on until 2 becomes 1. The sequence stops at 1.
Approach
The same transformation is applied again and again, and nobody knows in advance how many times. When the number of repetitions is not known, a while loop is the right tool: it keeps running as long as a condition holds. Here the condition is "the current value is greater than 1".
The current value changes on every pass, so it needs to live in a variable that can be reassigned. Inside the loop, an if / else chooses between the even rule and the odd rule. The value is printed once before the loop, for the starting term, and once at the end of every pass, for each new term.
Step by step
- Copy
ninto a variable. Method parameters in Scala are immutable, soncan't be reassigned. Declarevar num = nand changenuminstead. - Print the starting value. The sequence begins with
n, soprintln(num)comes before the loop. - Write the loop condition.
while (num > 1)keeps going until the value reaches 1. Testnum, notn, because onlynumchanges. - Handle the even case. If
num % 2 == 0, halve it withnum /= 2. Division of twoIntvalues drops the remainder, but an even number has none, so the result is exact. - Handle the odd case. In the
elsebranch, setnum = 3 * num + 1. - Print the new term. Put
println(num)after theelseblock and still inside the loop, so it runs for both cases. The final pass prints 1 and then the condition fails and the loop ends.
The solution in Scala
class Solution {
def printSeries(n: Int): Unit = {
var num = n
println(num)
while (num > 1) {
if (num % 2 == 0) {
num /= 2
} else {
num = 3 * num + 1
}
println(num)
}
}
}
Scala details in this solution:
varversusval. Avalcan't be reassigned, avarcan. The loop needs avarbecause the value changes on each pass. Its type,Int, is inferred fromn.num /= 2is shorthand. It meansnum = num / 2. The same pattern works for+=,-=and*=.- Integer division truncates.
7 / 2is 3 in Scala when both sides areInt. The code only divides even numbers, so nothing is lost. - The result type is
Unit. The method prints and returns nothing, so noreturnis needed. Intcan overflow. Values are 32-bit and wrap around silently on overflow. For small starting values the sequence stays well within range. Forn = 113383the sequence passes the largestInt, 2147483647, and the value goes negative after 120 steps (the value is then -1812855948). The loop conditionnum > 1is false for a negative number, so the loop ends right after printing that negative value, and the sequence is wrong. UsingLongavoids it: withLong, the sequence for 113383 peaks at 2482111348.
Tracing a second example
For n = 6 the variable num goes through these values: 6 is even and becomes 3; 3 is odd and becomes 10; then 5, 16, 8, 4, 2 and finally 1. The printed lines are 6, 3, 10, 5, 16, 8, 4, 2, 1. The first 6 comes from the print before the loop, and each later line comes from the print at the end of a pass. Once num is 1, the condition num > 1 is false and the method ends.
Edge cases and common mistakes
n = 1. The starting value is printed, the loop condition is false at once and the method prints just1.n = 0. It is not a positive integer, but the method handles it without looping forever: it prints0and stops, since 0 is not greater than 1.- Printing before the loop only. If the print inside the loop is missing, the output ends with the starting value alone.
- Printing the new value in only one branch. If
println(num)sits inside theifor theelseblock, terms from the other case are never printed. - Testing
nin the loop condition. Becausennever changes, a condition liken > 1never becomes false forn = 3, and the loop runs forever. - Printing 1 twice or not at all. The value 1 should appear once, as the final term. The condition
num > 1, with the print at the end of the pass, does exactly that.
Testing
printSeries(3)prints 3, 10, 5, 16, 8, 4, 2, 1, one per line.printSeries(1)prints1.printSeries(8)prints 8, 4, 2, 1.
