Problem overview
A magical number is what you get from a positive number by adding its digits again and again until one digit is left. The method magicNumber receives a number n and returns that single digit.
For n = 39, the digits add up to 3 + 9 = 12, which still has two digits. Adding again gives 1 + 2 = 3, so the answer is 3. For n = 928435, the first sum is 9 + 2 + 8 + 4 + 3 + 5 = 31, and the second is 3 + 1 = 4, so the answer is 4.
Approach: two small loops
The problem splits into two parts that are easy to write separately:
- A digit sum: given a number, add up its digits.
- A repeat: keep replacing the number with its digit sum until it has one digit.
The lesson writes the repeating part first, pretending that a method digitSum already exists, and then writes digitSum. This is a useful habit: decide what the helper must do, use it, and fill it in afterwards.
The repeating loop must end. A number with at least two digits is larger than its digit sum, so every pass makes n strictly smaller, and a positive whole number can't shrink forever.
To get the digits of a number, look at its last digit and then drop it. The remainder after dividing by 10 is the last digit: 39 % 10 is 9. Dividing by 10 and keeping the whole-number part drops that digit: 39 divided by 10 is 3.9, and its whole part is 3. Repeat until nothing is left.
Step by step
First, magicNumber:
- Loop while
nhas two or more digits. That is the same asn > 9. - Replace
nby its digit sum. Callthis.digitSum(n)and assign the result back ton. The call goes throughthisbecausedigitSumis a method of the class. - Return
nafter the loop. At that point it has one digit. The return belongs after the loop. Inside it, the method would stop after the first pass.
Then digitSum, written above magicNumber in the class:
- Declare the method with a parameter
nof typenumberand the return typenumber. Class methods are written without thefunctionkeyword. - Start the total. Declare
sumwithletand the typenumber, set to 0. No digit has been added yet, and the value changes on every pass. - Loop while digits remain. A positive number still has a digit exactly when
n > 0. - Add the last digit:
sum += n % 10. - Drop the last digit:
n = Math.floor(n / 10). In TypeScript,/on numbers gives a fraction when the division is not exact, as in39 / 10being3.9.Math.floorrounds down to the whole part. - Return
sumafter the loop.
The solution in TypeScript
class Solution {
digitSum(n: number): number {
let sum: number = 0;
while (n > 0) {
sum += n % 10;
n = Math.floor(n / 10);
}
return sum;
}
magicNumber(n: number): number {
while (n > 9) {
n = this.digitSum(n);
}
return n;
}
}
The TypeScript details worth a closer look:
- No integer division. A
numberis a double-precision float, so39 / 10is3.9, not3. WithoutMath.floor,nwould never reach 0 exactly, and the loop would carry on through ever smaller fractions. For positive numbers,Math.truncwould do the same job asMath.floor. %is the remainder. For positive whole numbers,n % 10is the last digit. For negative numbers the remainder keeps the sign ofn, which is one more reason the problem sticks to positive input.- Parameters can be reassigned. Both methods change their parameter
n. That only changes the method's own copy. The caller's variable, since a number is passed by value, is untouched. this.digitSum. Inside a method, a baredigitSum(n)would look for a function outside the class and fail to compile.this.digitSumfinds the method on the same object. Calling it asnew Solution().magicNumber(39)makesthisthe new object.- Precision limit. Whole numbers are exact only up to 2 to the power 53, about 9 quadrillion, so for larger values the digits themselves can be wrong. Typical inputs stay far below that.
Complexity
Let d be the number of digits of n, which is about the base-10 logarithm of n.
Time: O(d) in practice. The first digitSum call takes d steps. The result is at most 9 times d, so it has far fewer digits than that, and every later call is on a far shorter number. The first pass dominates, so the total is O(d), that is, O(log n).
Space: O(1). Only a few number variables are used.
Edge cases and common mistakes
- Already one digit.
5skips the loop and is returned as it is. - Multiples of 10.
10becomes1in one pass. - Forgetting
Math.floor.n / 10keeps the fraction, so39becomes3.9, then0.39, and so on. The loop keeps running for hundreds of passes until the value underflows to 0, and the sum it returns is wrong. - Loop condition
n >= 9. It is wrong for 9: the digit sum of 9 is 9, so the loop would never end. - Returning inside a loop. A
returninside the first loop endsmagicNumberafter one digit sum, which only works when that sum has one digit. - Calling the helper without
this.digitSum(n)fails to compile inside the class.
Testing
39gives3.928435gives4.5gives5.10gives1.99gives9.