204 lines
8.6 KiB
YAML
204 lines
8.6 KiB
YAML
title: Check if Number is a Sum of Powers of Three
|
||
slug: check-if-number-is-a-sum-of-powers-of-three
|
||
difficulty: medium
|
||
leetcode_id: 1780
|
||
leetcode_url: https://leetcode.com/problems/check-if-number-is-a-sum-of-powers-of-three/
|
||
categories:
|
||
- math
|
||
patterns:
|
||
- slug: greedy
|
||
is_optimal: true
|
||
|
||
function_signature: "def check_powers_of_three(n: int) -> bool:"
|
||
|
||
test_cases:
|
||
visible:
|
||
- input: { n: 12 }
|
||
expected: true
|
||
- input: { n: 91 }
|
||
expected: true
|
||
- input: { n: 21 }
|
||
expected: false
|
||
hidden:
|
||
- input: { n: 1 }
|
||
expected: true
|
||
- input: { n: 3 }
|
||
expected: true
|
||
- input: { n: 4 }
|
||
expected: true
|
||
- input: { n: 2 }
|
||
expected: false
|
||
- input: { n: 6 }
|
||
expected: false
|
||
- input: { n: 9 }
|
||
expected: true
|
||
- input: { n: 10 }
|
||
expected: true
|
||
|
||
description: |
|
||
Given an integer `n`, return `true` *if it is possible to represent* `n` *as the sum of distinct powers of three*. Otherwise, return `false`.
|
||
|
||
An integer `y` is a power of three if there exists an integer `x` such that `y == 3^x`.
|
||
|
||
constraints: |
|
||
- `1 <= n <= 10^7`
|
||
|
||
examples:
|
||
- input: "n = 12"
|
||
output: "true"
|
||
explanation: "12 = 3^1 + 3^2 = 3 + 9"
|
||
- input: "n = 91"
|
||
output: "true"
|
||
explanation: "91 = 3^0 + 3^2 + 3^4 = 1 + 9 + 81"
|
||
- input: "n = 21"
|
||
output: "false"
|
||
explanation: "21 cannot be represented as a sum of distinct powers of three."
|
||
|
||
explanation:
|
||
intuition: |
|
||
Think of this problem like converting a number to a different base system, specifically **base 3 (ternary)**.
|
||
|
||
In our familiar decimal system, we use digits 0-9. In binary (base 2), we use 0-1. In ternary (base 3), we use digits 0, 1, and 2. Each position represents a power of 3: the rightmost position is 3^0 = 1, the next is 3^1 = 3, then 3^2 = 9, and so on.
|
||
|
||
Here's the key insight: when we represent `n` in base 3, if all digits are either **0 or 1**, then `n` can be written as a sum of distinct powers of three. Why? Because a digit of 1 in position `i` means we include 3^i in our sum, and a digit of 0 means we don't. Each power of three is used at most once.
|
||
|
||
However, if any digit is **2**, that means we need to use some power of three *twice* in our sum, which violates the "distinct" requirement. For example, 21 in base 3 is `210` (2×9 + 1×3 + 0×1), meaning we'd need to use 3^2 = 9 twice — not allowed!
|
||
|
||
So the problem reduces to: **convert `n` to base 3 and check if all digits are 0 or 1**.
|
||
|
||
approach: |
|
||
We solve this using **Ternary (Base-3) Digit Checking**:
|
||
|
||
**Step 1: Repeatedly extract the last digit in base 3**
|
||
|
||
- Use `n % 3` to get the rightmost digit in base 3
|
||
- If this digit is 2, immediately return `false`
|
||
- A digit of 2 means we'd need to use that power of 3 twice
|
||
|
||
|
||
|
||
**Step 2: Remove the last digit**
|
||
|
||
- Use integer division `n //= 3` to remove the digit we just checked
|
||
- This shifts all remaining digits one position to the right
|
||
|
||
|
||
|
||
**Step 3: Repeat until n becomes 0**
|
||
|
||
- Continue the loop while `n > 0`
|
||
- If we check all digits without finding a 2, return `true`
|
||
|
||
|
||
|
||
This is essentially converting `n` to base 3 and validating each digit as we go, without storing the full representation.
|
||
|
||
common_pitfalls:
|
||
- title: Trying All Combinations of Powers
|
||
description: |
|
||
A brute force approach might try to enumerate all possible subsets of powers of 3 up to `n` and check if any subset sums to `n`.
|
||
|
||
With `n <= 10^7`, we have up to 15 powers of 3 (since 3^15 > 10^7). Checking all 2^15 = 32,768 subsets is technically feasible but unnecessary and much slower than the O(log n) ternary approach.
|
||
wrong_approach: "Enumerate all subsets of powers of 3"
|
||
correct_approach: "Check base-3 representation for digit 2"
|
||
|
||
- title: Forgetting Why Digit 2 Fails
|
||
description: |
|
||
It's important to understand *why* a digit of 2 means the answer is false. If the base-3 representation has a 2 in position `i`, that means `n` contains `2 × 3^i` as part of its decomposition.
|
||
|
||
Since we can only use each power of 3 at most once (distinct), we cannot represent `2 × 3^i` as a sum of distinct powers. For instance, `2 × 9 = 18` cannot be written as a sum of distinct powers of 3 that equals 18 — we'd need 9 + 9, but that uses 9 twice.
|
||
wrong_approach: "Not understanding the connection to base-3"
|
||
correct_approach: "Recognise that ternary digits > 1 require repeated powers"
|
||
|
||
- title: Integer Overflow with Large Powers
|
||
description: |
|
||
If you precompute all powers of 3 up to `n`, be careful not to overflow. In Python this isn't an issue, but in languages like C++ or Java, 3^20 exceeds 32-bit integer limits.
|
||
|
||
The iterative modulo approach avoids this entirely since we only work with values up to `n`.
|
||
wrong_approach: "Precomputing large powers without overflow checks"
|
||
correct_approach: "Use iterative n % 3 and n // 3 approach"
|
||
|
||
key_takeaways:
|
||
- "**Base conversion insight**: Many problems involving sums of distinct powers can be solved by examining the number in that base"
|
||
- "**Ternary representation**: A number is a sum of distinct powers of 3 if and only if its base-3 representation contains only 0s and 1s"
|
||
- "**Pattern recognition**: This same logic applies to powers of any base — e.g., all positive integers are sums of distinct powers of 2 (binary has only 0s and 1s by definition)"
|
||
- "**Efficiency through number theory**: Converting a brute force subset problem into a simple digit-checking problem reduces complexity dramatically"
|
||
|
||
time_complexity: "O(log n). We divide `n` by 3 in each iteration, so we process at most log_3(n) digits."
|
||
space_complexity: "O(1). We only use a single variable to track `n` as we modify it."
|
||
|
||
solutions:
|
||
- approach_name: Ternary Digit Check
|
||
is_optimal: true
|
||
code: |
|
||
def check_powers_of_three(n: int) -> bool:
|
||
# Check each digit in base-3 representation
|
||
while n > 0:
|
||
# Get the rightmost digit in base 3
|
||
if n % 3 == 2:
|
||
# Digit 2 means we'd need to use a power twice
|
||
return False
|
||
# Remove the digit we just checked
|
||
n //= 3
|
||
|
||
# All digits were 0 or 1
|
||
return True
|
||
explanation: |
|
||
**Time Complexity:** O(log n) — We divide by 3 each iteration, processing log_3(n) digits.
|
||
|
||
**Space Complexity:** O(1) — Only uses the input variable.
|
||
|
||
We convert `n` to base 3 on the fly, checking each digit. If any digit equals 2, we need that power of 3 twice, which violates the distinct requirement. If all digits are 0 or 1, the sum is valid.
|
||
|
||
- approach_name: Greedy Subtraction
|
||
is_optimal: false
|
||
code: |
|
||
def check_powers_of_three(n: int) -> bool:
|
||
# Find the largest power of 3 <= n
|
||
power = 1
|
||
while power * 3 <= n:
|
||
power *= 3
|
||
|
||
# Greedily subtract powers of 3
|
||
while n > 0 and power > 0:
|
||
if power <= n:
|
||
n -= power
|
||
power //= 3
|
||
|
||
return n == 0
|
||
explanation: |
|
||
**Time Complexity:** O(log n) — Two passes through powers of 3.
|
||
|
||
**Space Complexity:** O(1) — Only tracking `power` and `n`.
|
||
|
||
We greedily subtract the largest possible power of 3 at each step, using each power at most once. If we reduce `n` to 0, the answer is true. This is equivalent to the ternary approach but less elegant — it's harder to see why it works.
|
||
|
||
- approach_name: Subset Sum with Powers
|
||
is_optimal: false
|
||
code: |
|
||
def check_powers_of_three(n: int) -> bool:
|
||
# Generate all powers of 3 up to n
|
||
powers = []
|
||
power = 1
|
||
while power <= n:
|
||
powers.append(power)
|
||
power *= 3
|
||
|
||
# Use recursion to check if any subset sums to n
|
||
def can_sum(index: int, remaining: int) -> bool:
|
||
if remaining == 0:
|
||
return True
|
||
if remaining < 0 or index == len(powers):
|
||
return False
|
||
# Include or exclude current power
|
||
return (can_sum(index + 1, remaining - powers[index]) or
|
||
can_sum(index + 1, remaining))
|
||
|
||
return can_sum(0, n)
|
||
explanation: |
|
||
**Time Complexity:** O(2^k) where k = log_3(n) — potentially checking all subsets.
|
||
|
||
**Space Complexity:** O(k) — recursion depth and powers array.
|
||
|
||
This brute force approach tries all combinations of powers of 3. While it works for the given constraints (k <= 15), it's much slower than the O(log n) ternary approach. Included to show the connection to subset sum problems.
|