A clear explanation of reducing an integer to 1 with the fewest operations using greedy bit decisions.
Problem Restatement
We are given a positive integer n.
We need to turn n into 1 using the fewest operations.
The allowed operations are:
| Case | Operation |
|---|---|
n is even | Replace n with n / 2 |
n is odd | Replace n with either n + 1 or n - 1 |
Return the minimum number of operations needed.
For example:
n = 8The best path is:
8 -> 4 -> 2 -> 1So the answer is 3.
Input and Output
| Item | Meaning |
|---|---|
| Input | A positive integer n |
| Output | Minimum number of operations to reach 1 |
| Constraint | 1 <= n <= 2^31 - 1 |
Example function shape:
def integerReplacement(n: int) -> int:
...Examples
Example 1:
n = 8Since 8 is even:
8 -> 4 -> 2 -> 1The answer is:
3Example 2:
n = 7There are two natural choices:
7 -> 8 -> 4 -> 2 -> 1This takes 4 operations.
Or:
7 -> 6 -> 3 -> 2 -> 1This also takes 4 operations.
So the answer is:
4Example 3:
n = 4The path is:
4 -> 2 -> 1The answer is:
2First Thought: Recursion With Memoization
A direct way is to define:
dp(x)as the minimum number of operations needed to reduce x to 1.
If x == 1, the answer is 0.
If x is even:
dp(x) = 1 + dp(x // 2)If x is odd:
dp(x) = 1 + min(dp(x - 1), dp(x + 1))This is easy to reason about.
from functools import cache
class Solution:
def integerReplacement(self, n: int) -> int:
@cache
def dp(x: int) -> int:
if x == 1:
return 0
if x % 2 == 0:
return 1 + dp(x // 2)
return 1 + min(dp(x - 1), dp(x + 1))
return dp(n)This works in Python, but there is an even simpler iterative greedy solution.
Key Insight
Even numbers have no choice. We should always divide by 2.
The only decision happens when n is odd.
For an odd number, either n - 1 or n + 1 is even. After that, we can divide by 2.
We want to create more trailing zero bits, because trailing zeros allow repeated division by 2.
Look at the last two bits.
If an odd number ends in binary 01, subtracting 1 creates a number ending in 00.
Example:
9 = 1001
8 = 1000So for numbers ending in 01, decrement is good.
If an odd number ends in binary 11, adding 1 often creates more trailing zeros.
Example:
15 = 1111
16 = 10000So for numbers ending in 11, increment is usually good.
There is one important exception:
3 -> 2 -> 1This takes 2 operations.
But:
3 -> 4 -> 2 -> 1takes 3 operations.
So when n == 3, we subtract.
Algorithm
Initialize:
steps = 0While n != 1:
- If
nis even, divide by2. - Otherwise, if
n == 3, subtract1. - Otherwise, if the last two bits are
11, add1. - Otherwise, subtract
1. - Increase
steps.
The bit checks are:
n & 1to check odd or even, and:
n & 3to read the last two bits.
If:
n & 3 == 3then n ends in binary 11.
Correctness
When n is even, the only allowed operation is n / 2, so the algorithm must perform it.
When n is odd, both n - 1 and n + 1 are even. The next useful operation will divide by 2.
For odd numbers ending in binary 01, subtracting 1 creates a multiple of 4, while adding 1 creates a number only divisible by 2. Creating more factors of 2 cannot hurt, because division by 2 is the fastest way to reduce the number.
For odd numbers ending in binary 11, adding 1 creates a multiple of 4, except for the special case 3. This usually gives more immediate divisions by 2 than subtracting 1.
The case n = 3 must subtract because:
3 -> 2 -> 1is shorter than:
3 -> 4 -> 2 -> 1So the greedy choice always chooses the branch that creates the better even number for future halving, with the required exception for 3.
Therefore the algorithm reaches 1 in the minimum number of operations.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(log n) | Every one or two operations reduces the bit length or creates more division by 2 |
| Space | O(1) | Only n and steps are stored |
Implementation
class Solution:
def integerReplacement(self, n: int) -> int:
steps = 0
while n != 1:
if n % 2 == 0:
n //= 2
elif n == 3 or n % 4 == 1:
n -= 1
else:
n += 1
steps += 1
return stepsBitwise Implementation
The same logic can be written with bit operations:
class Solution:
def integerReplacement(self, n: int) -> int:
steps = 0
while n != 1:
if (n & 1) == 0:
n >>= 1
elif n == 3 or (n & 3) == 1:
n -= 1
else:
n += 1
steps += 1
return stepsCode Explanation
We count operations:
steps = 0The loop stops when n becomes 1:
while n != 1:If n is even, divide by 2:
if (n & 1) == 0:
n >>= 1If n is odd and equal to 3, subtract:
elif n == 3:
n -= 1If the last two bits are 01, subtract:
elif (n & 3) == 1:
n -= 1Otherwise the last two bits are 11, so add:
else:
n += 1Each loop performs one operation:
steps += 1Finally:
return stepsTesting
def test_solution():
s = Solution()
assert s.integerReplacement(1) == 0
assert s.integerReplacement(2) == 1
assert s.integerReplacement(3) == 2
assert s.integerReplacement(4) == 2
assert s.integerReplacement(7) == 4
assert s.integerReplacement(8) == 3
assert s.integerReplacement(15) == 5
assert s.integerReplacement(16) == 4
assert s.integerReplacement(2147483647) == 32
print("all tests passed")
test_solution()Test meaning:
| Test | Why |
|---|---|
1 | Already finished |
2 | One division |
3 | Special case where subtracting is better |
4 | Power of two |
7 | Both directions have optimal length |
8 | Main even example |
15 | Ends in binary 11, increment is useful |
16 | Power of two boundary |
2147483647 | Largest 32-bit signed input |