问题
I'm working on a problem where I'm given a number n, I have to find the next larger element with same number of set bits. While searching on Internet, I found an interesting piece of code which does this in few lines of code (BIT MAGIC) here:
unsigned nexthi_same_count_ones(unsigned a) {
/* works for any word length */
unsigned c = (a & -a);
unsigned r = a+c;
return (((r ^ a) >> 2) / c) | r);
}
But I want to understand the underlying logic about the algorithm that it will work always. All the boundary cases will be handled properly.
Can someone please explain the logic in simple steps.
Thanks
回答1:
In the next higher number, the leftmost 1
of the rightmost run of 1
s exchanges place with the 0
to its left, while the remaining 1
s move to the far right.
- The code isolates lowest
1
, - adds it to
a
(making carries ripple through to the next higher0
, inverting all those bits) - The ex-or gets the least significant run of ones, extended one position to the left.
- Shifting it right two positions takes its left boundary one position right of the original one
(leaving place for that one
0
from the high position), - dividing by the lowest
1
makes room for as many0
-bits more as there were on the right end ofa
.
回答2:
Let say we have a bit pattern such as
111100111 - representing 487 in decimal
to generate the next highest integer whilst preserving the number of 0's and 1's as in the input we need to find the first 0 bit from the right of the input that is followed by 1, and we need to toggle this bit to a 1. We then need to reduce the number of 1's on the right of this flip point by 1 to compensate for the bit we switched from a 0 to 1.
our new bit pattern will become 111101011 - 491 in decimal (we have preserved the no of bits set and not set as per the input)
int getNextNumber(int input)
{
int flipPosition=0;
int trailingZeros=0;
int trailingOnes=0;
int copy = input;
//count trailing zeros
while(copy != 0 && (copy&1) == 0 )
{
++trailingZeros;
//test next bit
copy = copy >> 1;
}
//count trailing ones
while(copy != 0 && (copy&1) == 1 )
{
++trailingOnes;
//test next bit
copy = copy >> 1;
}
//if we have no 1's we cannot form another patter with the same number of 1's
//which will increment the input, or if we have leading consecutive
//zero's followed by consecutive 1's up to the maximum bit size of a int
//we cannot increase the input whilst preserving the no of 0's and
//1's in the original bit pattern
if(trailingZeros + trailingOnes == 0 || trailingZeros + trailingOnes == 31)
return -1;
//flip first 0 followed by a 1 found from the right of the bit pattern
flipPosition = trailingZeros + trailingOnes+1;
input |= 1<<(trailingZeros+trailingOnes);
//clear fields to the right of the flip position
int mask = ~0 << (trailingZeros+trailingOnes);
input &= mask;
//insert a bit pattern to the right of the flop position that will contain
//one less 1 to compensate for the bit we switched from 0 to 1
int insert = flipPosition-1;
input |= insert;
return input;
}
来源:https://stackoverflow.com/questions/26594951/finding-next-bigger-number-with-same-number-of-set-bits