A clear explanation of reaching a target on a number line using cumulative sums and parity.
Problem Restatement
We start at position 0 on an infinite number line.
There is a destination at position target.
On move 1, we must move exactly 1 step.
On move 2, we must move exactly 2 steps.
On move 3, we must move exactly 3 steps.
In general, on move i, we must move exactly i steps.
For every move, we can choose either direction:
left or rightReturn the minimum number of moves needed to reach exactly target.
Input and Output
| Item | Meaning |
|---|---|
| Input | An integer target |
| Output | The minimum number of moves needed to reach target |
| Movement | On move i, move exactly i steps |
| Direction | Each move can go left or right |
Example function shape:
def reachNumber(target: int) -> int:
...Examples
Example 1:
target = 2Output:
3One way to reach 2 in three moves is:
0 -> 1 -> -1 -> 2The moves are:
| Move | Direction | Position |
|---|---|---|
1 | right 1 | 1 |
2 | left 2 | -1 |
3 | right 3 | 2 |
So the answer is 3.
Example 2:
target = 3Output:
2We can move right twice:
0 -> 1 -> 3The moves are:
| Move | Direction | Position |
|---|---|---|
1 | right 1 | 1 |
2 | right 2 | 3 |
So the answer is 2.
First Thought: Search All Paths
A direct idea is to explore all possible directions.
At move 1, we can go left or right.
At move 2, each of those choices branches again.
So after m moves, there are up to:
2^mpossible direction sequences.
That is too large.
We need to use the structure of the moves instead of searching every path.
Key Insight
The direction does not matter at first.
Suppose we always move right.
After m moves, our position is:
1 + 2 + 3 + ... + mThis sum is:
m * (m + 1) / 2Call this sum s.
If s == target, then m moves are enough.
If s > target, we may be able to flip some moves from right to left.
Flipping a move of size x changes the final position by:
2 * xbecause that move was contributing +x, and after flipping it contributes -x.
So the final position changes by an even number.
Therefore, after reaching a sum s, we can reach target exactly if:
s - target is evenIf s - target is odd, no set of flipped moves can fix the difference yet.
So we keep adding the next move until:
s >= target(s - target)is even
Why We Use Absolute Value
The number line is symmetric.
Reaching target = 5 takes the same number of moves as reaching target = -5.
Every right move can be replaced by a left move, and every left move can be replaced by a right move.
So we can solve only for:
target = abs(target)Algorithm
- Replace
targetwithabs(target). - Start with:
moves = 0position = 0
- Keep adding the next move while either:
position < targetposition - targetis odd
- Return
moves.
In code, the loop condition is:
while position < target or (position - target) % 2 == 1:Each iteration adds one more move:
moves += 1
position += movesWalkthrough
Take:
target = 2First use absolute value:
target = abs(2) = 2Now keep moving right.
| Move | Sum | Difference sum - target | Even? |
|---|---|---|---|
1 | 1 | -1 | No, sum is too small |
2 | 3 | 1 | No |
3 | 6 | 4 | Yes |
At move 3, the sum is 6.
The difference is:
6 - 2 = 4Since 4 is even, we can flip moves whose total is:
4 / 2 = 2That means flip the move of size 2.
Original all-right path:
+1 +2 +3 = 6Flip +2 into -2:
+1 -2 +3 = 2So 3 moves are enough.
Correctness
We solve for abs(target) because the movement rules are symmetric around 0. Any path to a positive target can be mirrored to reach the corresponding negative target with the same number of moves.
After m moves, if all moves go right, the farthest positive position is:
s = 1 + 2 + ... + mEvery other reachable position after m moves is formed by choosing some of these moves and flipping them from right to left.
If we flip moves with total length x, the final position becomes:
s - 2xTherefore, a target is reachable after m moves exactly when s >= target and s - target is even.
The algorithm increases m from 0 upward and stops at the first m satisfying these two conditions.
Since smaller values of m were checked and failed, the returned number of moves is minimal.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(sqrt(target)) | The cumulative sum grows quadratically in the number of moves |
| Space | O(1) | Only a few integer variables are used |
The sum after m moves is about m^2 / 2, so we need about sqrt(target) moves before crossing the target.
Implementation
class Solution:
def reachNumber(self, target: int) -> int:
target = abs(target)
moves = 0
position = 0
while position < target or (position - target) % 2 == 1:
moves += 1
position += moves
return movesCode Explanation
We first reduce the problem to the positive side:
target = abs(target)Then we keep track of how many moves we have taken:
moves = 0and the farthest position we can reach by moving right every time:
position = 0The loop continues while the current all-right sum cannot be adjusted to exactly reach target:
while position < target or (position - target) % 2 == 1:There are two bad cases.
First, if position < target, we have not gone far enough yet.
Second, if position - target is odd, then flipping moves cannot fix the difference, because every flip changes the position by an even amount.
Inside the loop, we add the next move:
moves += 1
position += movesWhen the loop stops, position >= target and position - target is even.
That means we can flip some moves to land exactly on target.
So we return:
return movesTesting
def run_tests():
s = Solution()
assert s.reachNumber(2) == 3
assert s.reachNumber(3) == 2
assert s.reachNumber(1) == 1
assert s.reachNumber(4) == 3
assert s.reachNumber(5) == 5
assert s.reachNumber(-2) == 3
assert s.reachNumber(-3) == 2
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
2 | Needs overshoot and parity correction |
3 | Reached directly by 1 + 2 |
1 | Single move |
4 | 1 + 2 + 3 = 6, flip 1 |
5 | Needs more moves because parity does not match early |
-2 | Confirms symmetry |
-3 | Negative direct mirror case |