A clear explanation of generating all powerful integers using bounded powers and a set.
Problem Restatement
We are given three integers:
x
y
boundAn integer is powerful if it can be written as:
x ** i + y ** jwhere:
i >= 0
j >= 0Return all powerful integers less than or equal to bound.
Each value should appear at most once, and the answer may be returned in any order.
The constraints are 1 <= x, y <= 100 and 0 <= bound <= 10^6.
Input and Output
| Item | Meaning |
|---|---|
| Input | Integers x, y, and bound |
| Output | All unique values x ** i + y ** j <= bound |
| Exponents | i and j are nonnegative integers |
| Order | Any output order is accepted |
Example function shape:
def powerfulIntegers(x: int, y: int, bound: int) -> list[int]:
...Examples
Example 1:
x = 2
y = 3
bound = 10Output:
[2, 3, 4, 5, 7, 9, 10]Explanation:
2 = 2 ** 0 + 3 ** 0
3 = 2 ** 1 + 3 ** 0
4 = 2 ** 0 + 3 ** 1
5 = 2 ** 1 + 3 ** 1
7 = 2 ** 2 + 3 ** 1
9 = 2 ** 3 + 3 ** 0
10 = 2 ** 0 + 3 ** 2Example 2:
x = 3
y = 5
bound = 15Output:
[2, 4, 6, 8, 10, 14]First Thought: Try Many Exponents
The expression has only two independent parts:
x ** i
y ** jSo we can generate all powers of x that are not too large, all powers of y that are not too large, and combine them.
This is efficient because bound is at most 10^6. Even when the base is 2, the number of powers is small.
For example:
2 ** 20 = 1048576So there are only about 20 useful powers of 2.
Key Insight
We only need powers whose value is at most bound.
The smallest possible other term is:
1because:
x ** 0 = 1
y ** 0 = 1So if one power is already greater than bound, it cannot produce a valid sum.
Use a set to avoid duplicates, because different exponent pairs can produce the same value.
Example:
x = 2
y = 3
2 ** 1 + 3 ** 1 = 5
2 ** 2 + 3 ** 0 = 5Both produce 5, but the answer should contain 5 once.
Handling x = 1 or y = 1
This is the main edge case.
If:
x = 1then:
x ** 0 = 1
x ** 1 = 1
x ** 2 = 1
...The power never changes.
So a loop that repeatedly multiplies by x would never stop.
To avoid this, after processing power 1, break when the base is 1.
The same applies to y.
Algorithm
- Create an empty set called
answer. - Start with:
power_x = 1- While
power_x <= bound:- Start with
power_y = 1. - While
power_x + power_y <= bound:- Add
power_x + power_yto the set. - If
y == 1, break. - Multiply
power_ybyy.
- Add
- If
x == 1, break. - Multiply
power_xbyx.
- Start with
- Return the set as a list.
Correctness
Every powerful integer has the form:
x ** i + y ** jfor some i >= 0 and j >= 0.
The outer loop enumerates every possible value of x ** i that can contribute to a valid sum. The inner loop enumerates every possible value of y ** j that can be added to the current x power without exceeding bound.
Whenever the sum is at most bound, the algorithm inserts it into the answer set. Therefore, every generated value is a valid powerful integer.
Conversely, take any valid powerful integer x ** i + y ** j <= bound. Since both terms are positive, x ** i <= bound and y ** j <= bound. The outer loop eventually reaches x ** i, and for that outer value, the inner loop eventually reaches y ** j. So the algorithm adds this valid integer.
The set removes duplicate sums from different exponent pairs. Therefore, the returned list contains exactly the unique powerful integers.
Complexity
Let:
A = number of powers of x up to bound
B = number of powers of y up to bound| Metric | Value | Why |
|---|---|---|
| Time | O(A * B) | We try each bounded power pair |
| Space | O(A * B) | The set may store many distinct sums |
When x > 1, A = O(log_x(bound)).
When x = 1, A = 1.
The same applies to y.
Implementation
class Solution:
def powerfulIntegers(self, x: int, y: int, bound: int) -> list[int]:
answer = set()
power_x = 1
while power_x <= bound:
power_y = 1
while power_x + power_y <= bound:
answer.add(power_x + power_y)
if y == 1:
break
power_y *= y
if x == 1:
break
power_x *= x
return list(answer)Code Explanation
We store unique results in a set:
answer = set()The first power is always:
power_x = 1because any number to the power 0 is 1.
The outer loop enumerates powers of x:
while power_x <= bound:For each power_x, we enumerate powers of y:
power_y = 1The inner loop continues only while the sum is valid:
while power_x + power_y <= bound:Each valid sum is inserted:
answer.add(power_x + power_y)If y == 1, multiplying by y would not change power_y, so we break:
if y == 1:
breakOtherwise, move to the next power:
power_y *= yThe same logic applies to x:
if x == 1:
breakFinally, return the results:
return list(answer)Testing
Because the output order may vary, compare sorted lists.
def run_tests():
s = Solution()
assert sorted(s.powerfulIntegers(2, 3, 10)) == [2, 3, 4, 5, 7, 9, 10]
assert sorted(s.powerfulIntegers(3, 5, 15)) == [2, 4, 6, 8, 10, 14]
assert sorted(s.powerfulIntegers(1, 2, 10)) == [2, 3, 5, 9]
assert sorted(s.powerfulIntegers(2, 1, 10)) == [2, 3, 5, 9]
assert sorted(s.powerfulIntegers(1, 1, 10)) == [2]
assert sorted(s.powerfulIntegers(2, 3, 1)) == []
assert sorted(s.powerfulIntegers(10, 10, 100)) == [2, 11, 20]
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
(2,3,10) | Main example |
(3,5,15) | Second example |
(1,2,10) | Handles x = 1 |
(2,1,10) | Handles y = 1 |
(1,1,10) | Both bases are 1 |
| Bound below minimum sum | No valid result |
(10,10,100) | Duplicate sums are removed |