Updated with newer answer and better test
Let\'s say I have the number 382 which is 101111110.
How could I randomly turn a bit which is not 0 to
You can turn on any bit by OR'ing it with 1 and turn it off by AND'ing with the bitwise complement.
Here's an example that selects a random 1-bit and turns it off.
var rand = new Random();
int myValue = 0x017E; // 101111110b
// identify which indexes are one-bits (if any, thanks Doc)
if( myValue > 0 )
{
var oneBitsIndexes = Enumerable.Range( 0, 31 )
.Where(i => ((myValue >> i) & 0x1) !=0).ToList();
// pick a random index and update the source value bit there from 1 to 0
myValue &= ~(1 << oneBitsIndexes[rand.Next(oneBitsIndexes.Count)]);
}
// otherwise, there are no bits to turn off...
Ok, a lot of wrong answers. Here's one that works:
^
Here's some sample code:
int myInt; // set this up with your original value
int myBitmask; // set this up with the bit mask via steps 1 and 2.
int randomlyZeroedBitInt = myInt ^ myBitmask;
Edit: On a fifth read of the question, I have a question in return: you are wanting to do which of the following:
Edit 2:
2 is correct,(15chars) – Fredou
In that case, my general algorithm stands; merely choose the bit in step 1 with internal logic. Alternatively, choose a fully random bit in step 1 and repeat until the value of myInt and randomlyZeroedBitInt are not equal.
Unfortunately either case means a more complex algorithm, as you'll either need to iterate over every bit in your value to determine which to flip, or you'll need to loop the algorithm until a bit is flipped.
Here is a version based on an algorithm from Bit Twiddling Hacks to select the nth set bit of an integer. For this case, we simply select n at random.
The code has been ported to C#, made to work directly on 32-bit signed integers, and count from the right instead of the left. Furthermore, the optimization to remove all branches has not been preserved here as it yielded slower code on my machine (Intel Core 2 Quad Q9450).
The description on the Bit Twiddling Hacks page does not give much insight into how the algorithm works. I have taken the time to step through and reverse engineer it and what I found is described in detail in the comments below.
In my tests, this algorithm performs very similarly to Kyteland's excellent flipRandomBit over input that is distributed randomly across the full range of 32-bit integers. However, flipRandomBit is slightly faster for numbers with significantly fewer set bits than cleared bits. Conversely, this algorithm is slightly faster for numbers with significantly more set bits than cleared bits.
The OP's benchmark consists entirely of small positive integers, which do not stress flipRandomBit's worst case. If this is an indication of the expected input, then all the more reason to prefer flipRandomBit.
static int ClearRandomSetBit(int input) {
///////////////////////////////////////////////////////////////////////
// ** Step 1 **
// Count the set bits
////////////////////////////////////////////////////////////////////////
// magic numbers
const int m2 = 0x55555555; // 1 zero, 1 one, ...
const int m4 = 0x33333333; // 2 zeros, 2 ones, ...
const int m8 = 0x0f0f0f0f; // 4 zeros, 4 ones, ...
// sequence of 2-bit values representing the counts of each 2 bits.
int c2 = input - ((input >> 1) & m2);
// sequence of 4-bit values representing the counts of each 4 bits.
int c4 = (c2 & m4) + ((c2 >> 2) & m4);
// sequence of 8-bit values representing the counts of each 8 bits.
int c8 = (c4 + (c4 >> 4)) & m8;
// count set bits in input.
int bitCount = (c8 * 0x1010101) >> 24;
///////////////////////////////////////////////////////////////////////////////////
// ** Step 2 **
// Select a random set bit to clear and find it using binary search with our
// knowledge of the bit counts in the various regions.
///////////////////////////////////////////////////////////////////////////////////
// count 16 right-most bits where we'll begin our search
int count = (c8 + (c8 >> 8)) & 0xff;
// position of target bit among the set bits
int target = random.Next(bitCount);
// distance in set bits from the current position to the target
int distance = target + 1;
// current bit position
int pos = 0;
// if the target is not in the right-most 16 bits, move past them
if (distance > count) { pos += 16; distance -= count; }
// if the target is not in the next 8 bits, move past them
count = (c8 >> pos) & 0xff;
if (distance > count) { pos += 8; distance -= count; }
// if the target is not in the next 4 bits, move past them
count = (c4 >> pos) & 0xf;
if (distance > count) { pos += 4; distance -= count; }
// if the target is not in the next 2 bits, move past them
count = (c2 >> pos) & 0x3;
if (distance > count) { pos += 2; distance -= count; }
// if the bit is not the next bit, move past it.
//
// Note that distance and count must be single bits by now.
// As such, distance is greater than count if and only if
// distance equals 1 and count equals 0. This obversation
// allows us to optimize away the final branch.
Debug.Assert((distance & 0x1) == distance);
Debug.Assert((count & 0x1) == count);
count = (input >> pos) & 0x1;
pos += (distance & (count ^ 1));
Debug.Assert((input & (1 << pos)) != 0);
return input ^ (1 << pos);
}
int val=382
int mask = ~(1 << N)
// this would turn-off nth bit (0 to 31)
NewVal = (int) ((uint)val & (uint)mask}
static Random random = new Random();
public static int Perturb(int data)
{
if (data == 0) return 0;
// attempt to pick a more narrow search space
int minBits = (data & 0xFFFF0000) == 0 ? 16 : 32;
// int used = 0; // Uncomment for more-bounded performance
int newData = data;
do
{
// Unbounded performance guarantees
newData &= ~(1 << random.Next(minBits));
// // More-bounded performance:
// int bit = 1 << random.Next(minBits);
// if ((used & bit) == bit) continue;
// used |= bit;
// newData &= ~bit;
} while (newData == data); // XXX: we know we've inverted at least one 1
// when the new value differs
return newData;
}
Update: added code above which can be used for more-bounded performance guarantees (or less unbounded if you want to think of it that way). Interestingly enough this performs better than the original uncommented version.
Below is an alternate approach that is fast but without bounded performance guarantees:
public static int FastPerturb(int data)
{
if (data == 0) return 0;
int bit = 0;
while (0 == (data & (bit = 1 << random.Next(32))));
return data & ~bit;
}
Here's a slightly more efficient version using bit twiddling.
public static int getBitCount(int bits)
{
bits = bits - ((bits >> 1) & 0x55555555);
bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
return ((bits + (bits >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
}
public static int flipRandomBit(int data)
{
int index = random.Next(getBitCount(data));
int mask = data;
for (int i = 0; i < index; i++)
mask &= mask - 1;
mask ^= mask & (mask - 1);
return data ^ mask;
}