Files
codetutor/backend/data/questions/check-if-number-is-a-sum-of-powers-of-three.yaml
2025-05-25 10:16:13 +01:00

177 lines
8.0 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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:
- greedy
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
&nbsp;
**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
&nbsp;
**Step 3: Repeat until n becomes 0**
- Continue the loop while `n > 0`
- If we check all digits without finding a 2, return `true`
&nbsp;
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.