A clear explanation of the Ugly Number problem using repeated division by the only allowed prime factors.
Problem Restatement
We are given an integer n.
Return True if n is an ugly number.
An ugly number is a positive integer whose prime factors are limited to:
2, 3, 5So a number is ugly if, after removing all factors of 2, 3, and 5, nothing remains except 1.
The problem constraints are:
-2^31 <= n <= 2^31 - 11 is considered ugly because it has no prime factors. Non-positive numbers are not ugly.
Input and Output
| Item | Meaning |
|---|---|
| Input | An integer n |
| Output | Boolean |
| Ugly number | Positive integer with no prime factors except 2, 3, and 5 |
| Special case | 1 is ugly |
Example function shape:
def isUgly(n: int) -> bool:
...Examples
Example 1:
n = 6Prime factorization:
6 = 2 * 3All prime factors are allowed.
Answer:
TrueExample 2:
n = 11 has no prime factors.
Since it has no forbidden prime factor, it is ugly.
Answer:
TrueExample 3:
n = 14Prime factorization:
14 = 2 * 7The factor 7 is not allowed.
Answer:
FalseExample 4:
n = 0Ugly numbers must be positive.
Answer:
FalseFirst Thought: Prime Factorization
A direct way is to factor n into primes, then check whether every prime factor is one of:
2, 3, 5This works, but full prime factorization is unnecessary.
We do not care what all factors are.
We only care whether any factor remains after removing all 2s, 3s, and 5s.
Key Insight
If n is ugly, then it can be reduced to 1 by repeatedly dividing by 2, 3, and 5.
For example:
n = 30Divide by 2:
30 / 2 = 15Divide by 3:
15 / 3 = 5Divide by 5:
5 / 5 = 1Since the final value is 1, 30 is ugly.
For a non-ugly number:
n = 14Divide by 2:
14 / 2 = 7Now 7 is not divisible by 2, 3, or 5.
The final value is 7, so 14 is not ugly.
Algorithm
- If
n <= 0, returnFalse. - For each allowed factor
pin[2, 3, 5]:- While
nis divisible byp, dividenbyp.
- While
- After removing all allowed factors:
- Return
Trueifn == 1. - Otherwise return
False.
- Return
Walkthrough
Use:
n = 12Start with allowed factors:
[2, 3, 5]Divide by 2 while possible:
12 / 2 = 6
6 / 2 = 3Now 3 is no longer divisible by 2.
Divide by 3:
3 / 3 = 1Now the value is 1.
Return:
TrueUse:
n = 42Divide by 2:
42 / 2 = 21Divide by 3:
21 / 3 = 77 cannot be divided by 2, 3, or 5.
Return:
FalseCorrectness
If the algorithm returns True, then after repeatedly dividing by 2, 3, and 5, the remaining value is 1. Therefore the original number was made only from factors 2, 3, and 5. So it has no forbidden prime factor and is ugly.
If the algorithm returns False, then after removing all possible factors of 2, 3, and 5, the remaining value is greater than 1. This remaining value must contain some prime factor other than 2, 3, or 5. Therefore the original number has a forbidden prime factor and is not ugly.
The algorithm also rejects all n <= 0, which is correct because ugly numbers are positive.
Therefore the algorithm returns the correct result.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(log n) | Each division reduces n by at least a factor of 2 |
| Space | O(1) | Only a few variables are used |
Implementation
class Solution:
def isUgly(self, n: int) -> bool:
if n <= 0:
return False
for factor in (2, 3, 5):
while n % factor == 0:
n //= factor
return n == 1Code Explanation
First handle non-positive input:
if n <= 0:
return FalseThis is necessary because ugly numbers are positive.
Then iterate through the only allowed prime factors:
for factor in (2, 3, 5):For each allowed factor, remove it completely:
while n % factor == 0:
n //= factorAfter all divisions, n == 1 means nothing forbidden remains.
return n == 1Testing
def run_tests():
s = Solution()
assert s.isUgly(6) is True
assert s.isUgly(1) is True
assert s.isUgly(14) is False
assert s.isUgly(0) is False
assert s.isUgly(-6) is False
assert s.isUgly(30) is True
assert s.isUgly(42) is False
assert s.isUgly(2 ** 10) is True
assert s.isUgly((2 ** 4) * (3 ** 3) * (5 ** 2)) is True
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
6 | Uses allowed factors 2 and 3 |
1 | No prime factors |
14 | Contains forbidden factor 7 |
0 | Non-positive input |
-6 | Negative input |
30 | Uses all allowed factors |
42 | Contains forbidden factor after division |
Power of 2 | Repeated allowed factor |
Product of 2, 3, and 5 | Larger ugly number |