Number and Math
What is this?
Three problems that each have an obvious slow solution and a short correct one. Testing every number up to n for primality is quadratic-ish; crossing off multiples is nearly linear. Checking every triple for the largest product is cubic; sorting once makes it two comparisons. Testing whether a number's only prime factors are 2, 3 and 5 by factorising it fully is unnecessary; dividing those factors out and checking what is left is three loops.
The pattern is the same each time: stop asking about every candidate and start eliminating whole classes at once.
→ is 1 left?"] B -->|"all primes below n"| D["sieve: cross off multiples
from p*p upward"] B -->|"largest product of three"| E["sort, then compare
top-3 vs two-smallest x largest"]
💡 Fun fact: the Sieve of Eratosthenes is around 2,200 years old and still the practical way to list primes below a few million. Two refinements make it fast: start crossing off at
p × p— every smaller multiple ofpalready has a smaller prime factor and was struck out earlier — and stop the outer loop at√n, because any composite belownhas a factor no larger than that. Those two details take it fromO(n log n)-ish toO(n log log n).
🔓 The 3 problems in this chapter are free. Sign in with Google or Microsoft to start solving.
The one-line idea: eliminate classes rather than testing individuals — divide known factors out, cross off multiples in bulk, or sort so that only two candidate answers remain to compare.
1. Divide the factors out
def is_ugly(n):
if n <= 0:
return False
for p in (2, 3, 5):
while n % p == 0:
n //= p
return n == 1
If dividing away every 2, 3 and 5 leaves exactly 1, the number had no other prime factors. The n <= 0 guard matters: zero would loop forever and negatives are excluded by definition.
2. Sieve, do not test
def count_primes(n):
if n < 3:
return 0
is_prime = [True] * n
is_prime[0] = is_prime[1] = False
p = 2
while p * p < n:
if is_prime[p]:
for multiple in range(p * p, n, p): # start at p*p, not 2p
is_prime[multiple] = False
p += 1
return sum(is_prime)
Starting at p × p is not a micro-optimisation — every multiple k × p with k < p has a prime factor smaller than p and was already crossed off when that smaller prime was processed.
3. Enumerate the sign cases
The largest product of three numbers is one of exactly two candidates:
- the three largest values, or
- the two most negative values times the largest.
The second wins when two large negatives multiply into a large positive. Sorting makes both readable in one line:
nums.sort()
return max(nums[-1] * nums[-2] * nums[-3],
nums[0] * nums[1] * nums[-1])
There is a linear alternative that tracks the three largest and two smallest in one pass — worth mentioning as the O(n) improvement once the sorted version is on the board.
4. A 30-second worked example
nums = [-10, -10, 1, 3, 2]
sorted: [-10, -10, 1, 2, 3]
candidate A — three largest: 1 x 2 x 3 = 6
candidate B — two smallest x max: -10 x -10 x 3 = 300 ✅
answer = 300
Taking only the three largest gives 6 and is wrong. The two negatives cancel, which is exactly the case the second candidate exists to catch.
5. Where you'll actually meet this
- Cryptography and hashing. Prime generation underpins key selection and table sizing.
- Numeric libraries. Smooth-number tests (only small prime factors) decide whether a fast Fourier transform size is efficient.
- Financial and scoring calculations. Extreme-product selections where negative values are meaningful.
- Load and shard sizing. Choosing sizes with convenient factorisations.
- Competitive and simulation code. Precomputed prime tables are a standard warm-up step.
6. Problems in this chapter
▶ Ugly Number
Decide whether a number's only prime factors are 2, 3 and 5. Divide each out repeatedly and check that 1 remains.
Pattern: factor elimination. Target: O(log n) time, O(1) space.
▶ Count Primes
Count primes below n. Sieve of Eratosthenes, crossing off from p × p with the outer loop bounded by √n.
Pattern: sieve. Target: O(n log log n) time, O(n) space.
▶ Maximum Product of Three Numbers
Largest product of any three values. Sort, then take the better of the top three and the two smallest times the largest.
Pattern: sign-case enumeration. Target: O(n log n) sorted, or O(n) with a single tracking pass.
7. Common pitfalls 🚫
- Looping forever on zero in the factor test. Guard
n <= 0first. - Starting the sieve at
2p. Correct but slower;p × pis the right lower bound. - Running the outer sieve loop to
n. It only needs to reach√n. - Off-by-one on "below n". Count Primes excludes
nitself, so the array hasnentries indexed0..n-1. - Taking only the three largest for the product. Two negatives can beat them.
- Assuming at least one positive value exists. An all-negative array still has a well-defined answer.
- Overflow. Three values near 10³ are fine, but in fixed-width languages the general case needs a 64-bit type.
8. Key takeaways
- Eliminate classes, not individuals. Cross off multiples; divide out factors.
p × pand√nare the two details that make a sieve fast.- Two candidates decide the extreme product, and the negative case is the one that gets missed.
- Sort for clarity, then offer the linear version — showing both is stronger than either alone.
- Guard the degenerate inputs. Zero, negatives and tiny
nare where these fail. - Know the standard complexities.
O(n log log n)for a sieve is worth saying precisely. - Why interviewers like it: short problems with well-known optimal solutions, so they reveal quickly whether you know the standard tools or are improvising.
Order: Ugly Number → Count Primes → Maximum Product of Three Numbers.
Core idea: Repeatedly divide the number by 2, then 3, then 5 as long as it stays divisible. Each division peels off one of the allowed prime factors. If everything that's left after the peeling is
1, the number was built only from 2s, 3s, and 5s — it's ugly. If anything else survives, some forbidden prime (7, 11, 13, …) was hiding inside.
Problem, rephrased
A number is "ugly" if it's a positive integer whose only prime factors come from the set {2, 3, 5}. So 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, … are all ugly. The number 1 counts as ugly because it has no prime factors at all — there's nothing to disqualify it. Anything that drags in a prime outside {2, 3, 5} — like 7, 11, or 13 — is not ugly.
Formally: given an integer n, return True if n is an ugly number and False otherwise. Watch the boundary — n must be positive, so 0 and all negatives are immediately False.
| Input | Output | Why |
|---|---|---|
6 |
True |
6 = 2 × 3 — only the primes 2 and 3 |
14 |
False |
14 = 2 × 7 — the 7 is a forbidden prime |
1 |
True |
no prime factors at all; nothing disqualifies it |
0 |
False |
not positive |
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson