Problem overview
The method magicNumber takes a positive Int named n. It adds up the digits of the number, then adds up the digits of that sum, and keeps going until a single digit is left. That digit is the answer.
For 39: the digits give 3 + 9 = 12, and 12 gives 1 + 2 = 3, so the result is 3. For 928435: 9 + 2 + 8 + 4 + 3 + 5 = 31, then 3 + 1 = 4, so the result is 4.
Approach: two small loops
The task splits into two parts that are easier to write separately:
- A helper,
digitSum, that adds up all the digits of a number. - An outer loop in
magicNumberthat applies the helper again and again while the number still has more than one digit.
To get the digits without turning the number into text, use arithmetic. The remainder after dividing by 10, written num % 10, is the last digit. Integer division by 10, num / 10, discards that digit and shifts the others one place to the right. Repeating this takes the digits off one at a time, from the last to the first, until nothing is left.
The outer loop always ends. A number with two or more digits is larger than its digit sum, so each pass makes the number smaller, and once it is below 10, the loop stops.
Step by step
The lesson writes the outer method first and assumes the helper exists.
- Copy the parameter into a variable. A method parameter is a
valand can't be reassigned, sovar num = ngives the loop something to change. - Loop while the number has at least two digits, which is exactly when
num > 9. - Replace the number by its digit sum:
num = digitSum(num). - Return
numafter the loop, when it has one digit. - Write
digitSumabove it, taking anIntnamednand returning anInt. It starts with its ownvar num = n, for the same reason as before. - Declare
sumas avarset to 0. No digit has been added yet. - Loop while
num > 0. A digit is still left exactly while the number is positive. - Add the last digit with
sum += num % 10. - Remove the last digit with
num /= 10. Dividing twoIntvalues drops the fraction, so only the integer part stays. - Return
sumafter the loop.
The solution in Scala
class Solution {
def digitSum(n: Int): Int = {
var num = n
var sum = 0
while (num > 0) {
sum += num % 10
num /= 10
}
return sum
}
def magicNumber(n: Int): Int = {
var num = n
while (num > 9) {
num = digitSum(num)
}
return num
}
}
The Scala details worth knowing:
- Parameters are
vals. Assigning tondirectly is a compile error, so each method copies it into a localvar. - Integer division truncates.
39 / 10is 3, not 3.9, and39 % 10is 9. Together they split 39 into its last digit and the rest. - Compound assignment.
sum += xmeanssum = sum + x, andnum /= 10meansnum = num / 10. Scala has no++or--operators, but it does have these. - Methods can call each other in any order inside a class, so
digitSummay be defined above or belowmagicNumber. returnis optional. Ending each method with justsumornummeans the same thing.
A worked trace
For n = 928435, the first call to digitSum peels off 5, 3, 4, 8, 2, 9, in that order, and returns 31. Because 31 is above 9, the outer loop calls digitSum(31), which returns 1 + 3 = 4. Now num is 4, the loop condition fails, and the method returns 4.
Complexity
Time: about O(log n). digitSum runs once per digit, and a number n has about the base-10 logarithm of n digits. The first call shrinks the value dramatically. For any Int, the digit sum is at most 82, so every later pass handles one or two digits. The total work is dominated by the first call.
Space: O(1). Only a few integer variables are used.
Edge cases and common mistakes
- Already one digit.
magicNumber(9)skips the loop and returns 9. - Multiples of 10.
10becomes 1 + 0 = 1. Zeros contribute nothing, anddigitSum(1230)is 6. - Zero.
magicNumber(0)returns 0, because 0 is not above 9. The task says the input is positive, so this is only a bonus. - Largest
Int.2147483647has digit sum 46, then 4 + 6 = 10, then 1, so the answer is 1. Nothing overflows, because the sums are tiny. - Negative numbers.
num % 10is negative for a negativenumand the loop conditionnum > 9is false, so a negative input is returned unchanged. - Using
num > 10. That would leave 10 unprocessed and return 10 instead of 1. - Using
while (num > 1)indigitSum. A leading digit of 1 is never added, sodigitSum(10)would return 0 instead of 1. The loop must continue whilenum > 0. - Returning inside the loop. The return belongs after the loop; inside it, the method would stop after the first pass.
Testing
39gives3.928435gives4.9gives9, and10gives1.2147483647gives1.
