The Game of Nim is a classic mathematical game of strategy. In this problem, you are given a list called piles
, where each element represents the number of stones in a separate pile. Two players take turns; on each turn, a player can remove any positive number of stones from a single pile. The player who removes the last stone wins the game.
You are asked to determine whether the first player (the one who starts the game) can guarantee a win, assuming both players play optimally.
piles
of positive integers.True
if the first player can force a win, False
otherwise.At first glance, you might consider simulating every possible move for both players, recursively checking all game states. However, this brute-force approach is extremely inefficient, especially as the number of piles or stones increases.
To optimize, we look for patterns or mathematical properties. The Game of Nim is well-known in combinatorial game theory, and it has a famous solution based on the bitwise XOR operation. The key insight is that, instead of tracking every possible move, you can use the XOR of all pile sizes to determine whether the first player can win.
The challenge lies in recognizing this pattern and understanding why XOR works for this game.
The solution relies on the Nim-sum, which is the XOR of all pile sizes. Here's why and how we use it:
piles
.
piles
array, XOR-ing each element together. Return True
if the result is not zero, False
otherwise.
This approach is efficient and elegant, requiring only a single pass through the array and constant space.
Let's consider the input piles = [3, 4, 5]
.
piles = [1, 1]
:
n
is the number of piles and k
is the maximum number of stones in a pile.
The Game of Nim is a beautiful example of how mathematical insight can turn an intractable brute-force problem into an elegant, efficient solution. By recognizing the significance of the XOR operation (Nim-sum), we can instantly determine the winning strategy for the first player. This approach is efficient, easy to implement, and highlights the power of pattern recognition in algorithm design.
def canWinNim(piles):
nim_sum = 0
for pile in piles:
nim_sum ^= pile
return nim_sum != 0
class Solution {
public:
bool canWinNim(vector<int>& piles) {
int nim_sum = 0;
for (int pile : piles) {
nim_sum ^= pile;
}
return nim_sum != 0;
}
};
class Solution {
public boolean canWinNim(int[] piles) {
int nimSum = 0;
for (int pile : piles) {
nimSum ^= pile;
}
return nimSum != 0;
}
}
function canWinNim(piles) {
let nimSum = 0;
for (let pile of piles) {
nimSum ^= pile;
}
return nimSum !== 0;
}