Problem overview
The function magicNumber receives a positive integer n. It adds up the digits of n, then adds up the digits of that sum, and keeps going until a single digit remains. That digit is the answer.
In the first example, 39 becomes 3 + 9 = 12, then 1 + 2 = 3, so the result is 3. In the second, 928435 becomes 9 + 2 + 8 + 4 + 3 + 5 = 31, then 3 + 1 = 4, so the result is 4.
Approach: two small loops
The problem splits into two jobs, and each one is a short loop.
- Outer job: as long as the number has two or more digits, replace it by its digit sum.
- Inner job: compute the digit sum of a number.
The lesson writes the outer job first and assumes a function digitSum exists. This is a useful habit: decide what you need from a helper before you write it. The helper is then built to match.
The digit sum itself uses two integer operations. n % 10 is the last digit, and n / 10 removes it, because integer division drops the remainder. Repeating until the number reaches 0 visits every digit once. For 928435, the last digits come out as 5, 3, 4, 8, 2, 9.
The outer loop always ends. For any number of two or more digits, the digit sum is smaller than the number, so the value strictly decreases until it falls to a single digit.
Step by step
- Make
nchangeable. Kotlin parameters are read-only. The linevar n = ncreates a local variable with the same name that starts with the parameter's value and can be reassigned. - Loop while
nhas two or more digits. That is exactly whenn > 9. - Replace
nby its digit sum:n = digitSum(n). - Return
nafter the loop. Returning inside the loop would stop after the first round. - Write
digitSum(n: Int): Intwith the samevar n = nline, for the same reason. - Start
sumat 0 as avar, because nothing has been added yet and the value changes on each pass. - Loop while
n > 0, that is, while at least one digit is left. - Add the last digit with
sum += n % 10. - Remove the last digit with
n /= 10. Dividing twoIntvalues in Kotlin gives anInt, so928435 / 10is92843. - Return
sumafter the loop.
Tracing an example
Here is magicNumber(928435) as the program runs it. The outer loop sees n = 928435, which is greater than 9, so it calls digitSum. Inside, the inner loop takes the last digit and removes it, again and again:
n = 928435 last digit 5 sum = 5
n = 92843 last digit 3 sum = 8
n = 9284 last digit 4 sum = 12
n = 928 last digit 8 sum = 20
n = 92 last digit 2 sum = 22
n = 9 last digit 9 sum = 31
n = 0 loop ends, digitSum returns 31
The outer loop now holds n = 31, still above 9, so it calls digitSum(31), which returns 4. The check 4 > 9 fails, and the function returns 4.
The solution in Kotlin
class Solution {
fun digitSum(n: Int): Int {
var n = n
var sum = 0
while (n > 0) {
sum += n % 10
n /= 10
}
return sum
}
fun magicNumber(n: Int): Int {
var n = n
while (n > 9) {
n = digitSum(n)
}
return n
}
}
Kotlin details worth knowing:
- Shadowing.
var n = ndeclares a new variable that hides the parameter inside the function body. The compiler warns about the shadowing, but the code is valid, and the right-handnis the parameter. The caller's value is never touched, since anIntis passed by value. - Compound assignment.
sum += n % 10andn /= 10are shorthand forsum = sum + n % 10andn = n / 10. The%operator binds tighter than+=, so the remainder is computed first. - Integer division truncates.
7 / 10is0, not0.7. That is what makes the last step of the digit loop, from a single digit to 0, work. - Member functions can call each other.
magicNumbercallsdigitSumby name, without any prefix. Their order inside the class doesn't matter.
A shortcut for the curious
For a positive n, the repeated digit sum equals 1 + (n - 1) % 9, a result from number theory about remainders after division by 9. It gives the answer in constant time. The loops in this lesson are the better way to learn: they work from the definition and practise while loops and helper functions.
Complexity
Time: O(log n). A number n has about log₁₀(n) digits, and one call to digitSum does a constant amount of work per digit. After the first call the value is small: an Int has at most 10 digits, so its digit sum is at most 90. Every later round works on a one- or two-digit number and takes only a few steps.
Space: O(1). Only a few integer variables are used.
Edge cases and common mistakes
- A single digit.
7skips the outer loop and returns7. - Multiples of 9.
99becomes18, then9. The answer for any positive multiple of 9 is 9, never 0. - Powers of 10.
1000has digit sum1. The zeros contribute nothing. - Largest
Int.Int.MAX_VALUE, 2147483647, has the digit sum 46, then 10, then 1, somagicNumberreturns1. No overflow happens, since digit sums are small. - Zero. The statement is about positive numbers. With 0, both loops are skipped and the function returns
0. - Using
n >= 9in the outer loop. For9the digit sum is9again, so the loop never ends. Usen > 9. - Returning inside the loop. The function would return after one round, giving
12for39. - Declaring
sumwithval. The compiler rejectssum += ...on aval.
Testing
39gives3.928435gives4.7gives7.99gives9.1000gives1.2147483647gives1.