I have a 32bit random value (say 631
).
0...0000001001110111
Each of these bits is a flag. I would like to return a random flag from these bits in, if at all possible, O(1) operation. How would I select the bit position 0, 1, 2, 4, 5, 6 or 9 (or it's corresponding value 1, 2, 4, 16, 32, 64, 512) from the given value 631
? Preferably with as least possible bias towards some bits.
Things I came up with:
Above is not O(1) and possibly need multiple iterations if we happen to only 'hit' the 0 bits.
Again, above is not O(1) unfortunately.
I'm pretty sure this must be possible with some bithisfting/masking/magic somehow...
Edit:
As CodeCaster suggested; this will give me all set bit values:
int myvalue = 631;
var matchingmasks = Enumerable.Range(0, 32)
.Select(i => 1 << i)
.Where(i => (myvalue & i) == i)
.ToArray();
From the resulting array I can pick a random element and I'll have found my 'random' bit (flag) from the given value. However, this still requires a (hidden, because Linq) for-loop, 'brute-forcing' each possible bit, memory allocations for the resulting array etc.
First off, I would suggest doing this the easy, straightforward, obvious way that you suggest in the question: make an array of values, choose an element at random. Yes this allocates memory and whatnot. Optimize for the code being readable and correct first; only when you have a demonstrated performance problem should you optimize it.
If you do want to optimize it down to bit twiddling, this page is my go-to resource: http://graphics.stanford.edu/~seander/bithacks.html
The algorithms you'll want here are:
I note that a key feature of many of these algorithms is that they are branchless. When you are trying to wring the last ounce of performance out of an algorithm, remember that every "if" kills performance. An "if" means that there is code in the cache that is NOT running, because you branched away from it, and therefore you are making a cache miss more likely. An "if" means there is an opportunity for the branch predictor to make the wrong choice. At the CLR level, every "if" means more basic blocks, which means more work for the jitter to do its flow analysis. And so on.
You can simply create the masks on beforehand, and select the masks that match the source value:
uint source = 631;
uint[] masks = Enumerable.Range(0, 32).Select(i => (uint)1 << i).ToArray();
uint[] matchingMask = masks.Where(m => (m & source) == m).ToArray();
Now matchingMask
contains the values that make up your source
value, in this case: 1, 2, 4, 16, 32, 64, 512
.
Then from matchingMask
you can select a random element.
If you want the bit position instead, you could use the indexing Select()
overload like this:
var matchingMask = masks.Select((m, i) => new { Index = i, Mask = m})
.Where(m => (m.Mask & source) == m.Mask)
.ToArray();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With