Problem overview
The function printSeries receives an Int named n and prints every value that n takes in the Collatz sequence, one per line, starting with n itself and ending at 1. Each new term comes from the previous one by a simple rule:
- if the term is even, the next term is half of it;
- if the term is odd, the next term is three times it plus one.
It is conjectured, though not proven, that every positive starting number eventually reaches 1. For n = 3 the output is 3, 10, 5, 16, 8, 4, 2, 1: 3 is odd, so it becomes 10; 10 is even, so it becomes 5; and so on until the sequence arrives at 1.
Approach: repeat a transformation until a condition fails
The number of steps isn't known in advance. It differs from one starting value to the next, so a loop that runs a fixed number of times doesn't fit. A while loop does: it repeats as long as its condition is true, and the condition here is "the current term is greater than 1".
Inside the loop, an if and else choose between the two rules. Because exactly one rule applies to each term, the two branches are alternatives. After either branch the new term is printed, so the output has one line for the starting value and one for each step.
Step by step
- Copy the parameter into a variable. Function parameters in Kotlin are read-only, so
ncan't be reassigned inside the function. Declarevar num = nand updatenuminstead. - Print the starting value. The sequence begins with
n, so it is printed before the loop. - Write the loop with the condition
num > 1. The sequence is finished when it reaches 1. The condition must testnum, notn, because onlynumchanges. - Handle the even case. When
num % 2 == 0, halvenum. Integer division drops any remainder, but an even number has none, so the result is exact. - Handle the odd case in the
elsebranch: setnumto3 * num + 1. - Print after the branches but still inside the loop. The statement then runs for both cases, once for every new term.
The solution in Kotlin
class Solution {
fun printSeries(n: Int) {
var num = n
println(num)
while (num > 1) {
if (num % 2 == 0) {
num /= 2
} else {
num = 3 * num + 1
}
println(num)
}
}
}
The Kotlin details:
valversusvar. A parameter behaves like aval: it can be read but not assigned. A localvarcan be reassigned, which is what the loop needs.- The type is inferred.
var num = ngets the typeIntfromn. num /= 2. This is shorthand fornum = num / 2. Between twoIntvalues,/is integer division, which truncates.- Return type. The function returns nothing useful, so it has the return type
Unit, which Kotlin lets you leave out.
Tracing n = 6
The table shows the state at the top of each pass of the loop and the term printed by that pass.
| num at the loop test | Even? | New num | Printed |
|---|---|---|---|
| 6 | yes | 3 | 3 |
| 3 | no | 10 | 10 |
| 10 | yes | 5 | 5 |
| 5 | no | 16 | 16 |
| 16 | yes | 8 | 8 |
| 8 | yes | 4 | 4 |
| 4 | yes | 2 | 2 |
| 2 | yes | 1 | 1 |
| 1 | condition false, the loop ends | ||
The starting 6 was printed before the loop, so the full output is 6, 3, 10, 5, 16, 8, 4, 2, 1. Notice that the last printed value is always 1, and that the loop condition is checked again before every pass, never in the middle of one.
Complexity
The number of terms depends on the starting value in an irregular way. Numbers of similar size can give very different lengths: the sequence for 27 has 111 steps, while 32 needs only 5. No simple bound in terms of n is known, and whether every sequence ends at all is the open question the conjecture is about. The space used is O(1): a single variable, with output printed as it is produced.
Edge cases and common mistakes
n = 1. The starting value is printed and the loop condition is false, so the output is just1.- Powers of two.
n = 8only halves:8, 4, 2, 1. - Printing in the wrong place. Putting the second
printlninside only one of the branches skips terms. Putting it before theifprints the old value twice and never prints the final1. - Testing
nin the loop condition. Sincennever changes, the loop would not end for any start above 1. - Overflow. An
Intholds values only up to 2,147,483,647, and the odd rule can make terms grow large. Forn = 113383a term overflows after 120 steps, wraps to the negative number-1812855948and is printed. Because a negative number is not greater than 1, the loop then stops. UsingLongfornumavoids the problem, but the lesson's parameter is anInt. - Zero and negative inputs. The task doesn't expect them. They print the starting value and stop, because the loop condition is false.
Testing
n = 3prints3, 10, 5, 16, 8, 4, 2, 1.n = 6prints6, 3, 10, 5, 16, 8, 4, 2, 1.n = 1prints only1.