Skip to content

LeetCode 263: Ugly Number

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, 5

So 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 - 1

1 is considered ugly because it has no prime factors. Non-positive numbers are not ugly.

Input and Output

ItemMeaning
InputAn integer n
OutputBoolean
Ugly numberPositive integer with no prime factors except 2, 3, and 5
Special case1 is ugly

Example function shape:

def isUgly(n: int) -> bool:
    ...

Examples

Example 1:

n = 6

Prime factorization:

6 = 2 * 3

All prime factors are allowed.

Answer:

True

Example 2:

n = 1

1 has no prime factors.

Since it has no forbidden prime factor, it is ugly.

Answer:

True

Example 3:

n = 14

Prime factorization:

14 = 2 * 7

The factor 7 is not allowed.

Answer:

False

Example 4:

n = 0

Ugly numbers must be positive.

Answer:

False

First Thought: Prime Factorization

A direct way is to factor n into primes, then check whether every prime factor is one of:

2, 3, 5

This 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 = 30

Divide by 2:

30 / 2 = 15

Divide by 3:

15 / 3 = 5

Divide by 5:

5 / 5 = 1

Since the final value is 1, 30 is ugly.

For a non-ugly number:

n = 14

Divide by 2:

14 / 2 = 7

Now 7 is not divisible by 2, 3, or 5.

The final value is 7, so 14 is not ugly.

Algorithm

  1. If n <= 0, return False.
  2. For each allowed factor p in [2, 3, 5]:
    • While n is divisible by p, divide n by p.
  3. After removing all allowed factors:
    • Return True if n == 1.
    • Otherwise return False.

Walkthrough

Use:

n = 12

Start with allowed factors:

[2, 3, 5]

Divide by 2 while possible:

12 / 2 = 6
6 / 2 = 3

Now 3 is no longer divisible by 2.

Divide by 3:

3 / 3 = 1

Now the value is 1.

Return:

True

Use:

n = 42

Divide by 2:

42 / 2 = 21

Divide by 3:

21 / 3 = 7

7 cannot be divided by 2, 3, or 5.

Return:

False

Correctness

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

MetricValueWhy
TimeO(log n)Each division reduces n by at least a factor of 2
SpaceO(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 == 1

Code Explanation

First handle non-positive input:

if n <= 0:
    return False

This 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 //= factor

After all divisions, n == 1 means nothing forbidden remains.

return n == 1

Testing

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:

TestWhy
6Uses allowed factors 2 and 3
1No prime factors
14Contains forbidden factor 7
0Non-positive input
-6Negative input
30Uses all allowed factors
42Contains forbidden factor after division
Power of 2Repeated allowed factor
Product of 2, 3, and 5Larger ugly number