2

This a fairly known problem ( similar question: number of setbits in a number and a game based on setbits but answer not clear ):

The beauty of a number X is the number of 1s in the binary representation of X. Two players are plaing a game. There is a number n written on a blackboard. The game is played as following:

Each time a player chooses an integer number (0 <= k) so that 2^k is less than n and (n-2^k) is equally as beautiful as n. Then n is removed from blackboard and replaced with n-2^k instead. The player that cannot continue the game (there is no such k that satisfies the constrains) loses the game.

The First player starts the game and they alternate turns. Knowing that both players play optimally must specify the winner.

Now the solution I came up with is this:

Moving a 1 bit to its right, is subtracting the number by 2^p where ( p = position the bit moved to - 1). Example: 11001 --> 25 now if I change it to 10101 ---> 21 ( 25-(2^2))

A player can't make 2 or more such right shift in 1 round (not the programmatic right shift) as they can't sum to a power of 2. So the player are left with moving the set bit to some position to its right just once each round. This means there can be only R rounds where R is the number of times a set bit can be moved to a more right position. So the winner will always be the 1st player if R is Odd number and 2nd player if R is even number.

Original#: 101001 41
after 1st: 11001 25 (41-16)
after 2nd: 10101 21 (25-4)
after 1st:  1101 13 (21-8)
after 2nd:  1011 11 (13-2)
after 1st:   111  7 (11-4) --> the game will end at this point

I'm not sure about the correctness of the approach, is this correct? or am I missing something big?

Apriori
  • 2,308
  • 15
  • 16
broun
  • 2,483
  • 5
  • 40
  • 55

3 Answers3

2

Your approach is on the right track. The observation to be made here is that, also as illustrated in the example you gave, the game ends when all ones are on the least significant bits. So we basically need to count how many swaps we need to make the zeros go to the most significant bits.

Let's take an example, say the initial number from which game starts is 12 the the game state is as follows:

Initial state 1100 (12) -> 
A makes move 1010 (10) -> 
B makes move 1001 (9) -> 
A makes move 0101 (5) -> 
B makes 0011 (3)
A cannot move further and B wins

This can be programmatically (java program v7) achieved as

public int identifyWinner (int n) {

    int total = 0, numZeros = 0;

    while (n != 0) {
        if ((n & 0x01) == 1) {
            total += numZeros;
        } else {
            numZeros++;
        }
        n >>= 1;
    }

    return (total & 0b1) == 1 ? 1 : 2;
}

Also to note that even if there are multiple choices available with a player to make the next move, as illustrated below, the outcome will not change though the intermediate changes leading to outcome may change.

Again let us look at the state flow taking the same example of initial number 12

Initial state 1100 (12) -> 
A makes move 1010 (10) -> 
(B here has multiple choices) B makes move 0110 (6)
A makes move 0101 (5) -> 
B makes 0011 (3) 
A cannot move further and B wins

A cannot move further as for no k (k >=0 and n < 2**k so k =0, 1 are the only plausible choices here) does n-2^k has same beauty as n so B wins.

Multiple choices are possible with 41 as starting point as well, but A will win always (41(S) -> 37(A) -> 35(B) -> 19(A) -> 11(B) -> 7(A)).

Hope it helps!

Harshdeep
  • 5,614
  • 10
  • 37
  • 45
0

Yes, each turn a 1 can move right if there is a 0 to its right.

But, no, the number of moves is not related to number of zeros. Counterexample:

101 (1 possible move)

versus

110 (2 possible moves)
aka.nice
  • 9,100
  • 1
  • 28
  • 40
  • aah, finally some response !! :) Thanks. Yeah, thats what i meant by every set bit can move to a righter position. "This means there can be only r rounds where r is the number times a set bit can be moved to a righter position". In 110 example, 1st round would change to 101 and next round would change to 011. Please let me know if the question needs to be reworded. – broun Apr 16 '14 at 04:02
  • @maver1k You wrote "So the winner will always be the 1st player if there are Odd number of 0s and 2nd player if there are even number of 0s.", that's what I answered to. – aka.nice Apr 16 '14 at 20:55
0

The number of moves in the game is the sum of the total 1's to the left of each 0. (Or conversely the sum of the total 0's to the right of each 1.)
(i.e. 11000 has 2 + 2 + 2 = 6 moves, but 10100 has 1 + 2 + 2 = 5 moves because one 0 has one less 1 to its right)

The winner of the game will be the first player if the total moves in the game is odd, and will be the second player if the number of moves in the game is even.

Proof:

On any given move a player must choose a bit corresponding to a 0 immediately to the right of a 1. Otherwise the total number of 1's will increase if a bit corresponding to a different 0 is chosen, and will decrease if a bit corresponding to a 1 is chosen. Such a move will result in the 1 to the right of the corresponding chosen bit being moved one position to its right.

Given this observation, each 1 has to move through every 0 to its right; and every 0 it moves through consumes one move. Note that regardless of the choices either player makes on any given move, the total number of moves in the game remains fixed.

Since Harshdeep has already posted a correct solution looping over each bit (the O(n) solution), I'll post an optimized divide and conquer O(log(n)) solution (in C/C++) reminiscent of a similar algorithm to calculate Hamming Weight. Of course using Big-Oh to describe the algorithm here is somewhat dubious since the number of bits is constant.

I've verified that the below code on all 32-bit unsigned integers gives the same result as the linear algorithm. This code runs over all values in order in 45 seconds on my machine, while the linear code takes 6 minutes and 45 seconds.

Code:

bool FastP1Win(unsigned n) {
      unsigned t;

      // lo: 0/1 count parity
      // hi: move count parity
      // 00 -> 00 : 00 >>1-> 00 &01-> 00 ; 00 |00-> 00 ; 00 &01-> 00 &00-> 00 *11-> 00 ^00-> 00
      // 01 -> 01 : 01 >>1-> 00 &01-> 00 ; 01 |00-> 01 ; 01 &01-> 01 &00-> 00 *11-> 00 ^01-> 01
      // 10 -> 11 : 10 >>1-> 01 &01-> 01 ; 10 |01-> 11 ; 10 &01-> 00 &01-> 00 *11-> 00 ^11-> 11
      // 11 -> 00 : 11 >>1-> 01 &01-> 01 ; 11 |01-> 11 ; 11 &01-> 01 &01-> 01 *11-> 11 ^11-> 00
      t  = (n >> 1) & 0x55555555;
      n  = (n | t) ^ ((n & t & 0x55555555) * 0x3);

      t  = n << 2;                          // move every right 2-bit solution to line up with the every left 2-bit solution
      n ^= ((n & t & 0x44444444) << 1) ^ t; // merge the right 2-bit solution into the left 2-bit solution
      t  = (n << 4);                        // move every right 4-bit solution to line up with the every left 4-bit solution
      n ^= ((n & t & 0x40404040) << 1) ^ t; // merge the right 4-bit solution into the left 4-bit solution (stored in the high 2 bits of every 4 bits)
      t  = n << 8;                          // move every right 8-bit solution to line up with the every left 8-bit solution
      n ^= ((n & t & 0x40004000) << 1) ^ t; // merge the right 8-bit solution into the left 8-bit solution (stored in the high 2 bits of every 8 bits)
      t  = n << 16;                         // move every right 16-bit solution to line up with the every left 16-bit solution
      n ^= ((n & t) << 1) ^ t;              // merge the right 16-bit solution into the left 16-bit solution (stored in the high 2 bits of every 16 bits)
      return (int)n < 0;                    // return the parity of the move count of the overall solution (now stored in the sign bit)
}

Explanation:
To find number of moves in the game, one can divide the problem into smaller pieces and combine the pieces. One must track the number of 0's in any given piece, and also the number of moves in any given piece.

For instance, if we divide the problem into two 16-bit pieces then the following equation expresses the combination of the solutions:

totalmoves = leftmoves + rightmoves + (rightzeros * (16 - leftzeros)); // 16 - leftzeros yields the leftones count

Since we don't care about the total moves, just weather the value is even or odd (the parity) we only need to track the parity.

Here is the truth table for the parity of addition:

even + even = even
even + odd  = odd
odd  + even = odd
odd  + odd  = even

Given the above truth table, the parity of addition can be expressed with an XOR.

And the truth table for the parity of multiplication:

even * even = even
even * odd  = even
odd  * even = even
odd  * odd  = odd

Given the above truth table, the parity of multiplication can be expressed with an AND.

If we divide the problem into pieces of even size, then the parity of the zero count, and the one count, will always be equal and we need not track or calculate them separately.

At any given stage of the algorithm we need to know the parity of the zero/one count, and the parity of the number of moves in that piece of the solution. This requires two bits. So, lets transform every two bits in the solution so that the high bit becomes the move count parity, and the low bit becomes the zero/one count parity.

This is accomplished with this computation:

unsigned t;
t  = (n >> 1) & 0x55555555;
n  = (n | t) ^ ((n & t & 0x55555555) * 0x3);

From here we combine every adjacent 2-bit solution into a 4-bit solution (using & for multiplication, ^ for addition, and the relationships described above), then every adjacent 4-bit solution into a 8-bit solution, then every adjacent 8-bit solution into a 16-bit solution, and finally every adjacent 16-bit solution into a 32-bit solution.

At the end, only the parity of the number of moves is returned stored in the second least significant bit.

Community
  • 1
  • 1
Apriori
  • 2,308
  • 15
  • 16
  • Looking more closely at some of the other solutions, Harshdeep's code is correct. If you want to optimise it a 256 sized lookup table could be used to handle 1 byte at a time. (or a lookup table of size 16 to handle 4 bits at a time) – Apriori Jun 01 '14 at 04:25
  • After some further thought I realized no look-up tables are needed. Since you only need to track the parity of the move count, not the actual move count, it can be done more efficiently with other approaches. Solution now posted above. – Apriori Jun 02 '14 at 01:40