I have std::bitset<32> word
and I want to choose randomly and index (0-31) of some bit which is 1. How can I do that without loops and counters. Is there any std::algorithm
suitable for that?
If it's easier I can convert the bitset
to string or int and make it on the string or int.
Asked
Active
Viewed 1,645 times
4

manlio
- 18,345
- 14
- 76
- 126

Hanna Khalil
- 975
- 1
- 10
- 28
-
2http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution – chris May 26 '16 at 12:08
-
1Thank you chris. but how does that help? Not all bits are 1 so not all indices between 0-31 are valid – Hanna Khalil May 26 '16 at 12:09
-
This sounds like an XY-problem. What are you actually trying to accomplish? – NathanOliver May 26 '16 at 12:10
-
@HannaKhalil Well, using a `&` operation with a random generated value? – πάντα ῥεῖ May 26 '16 at 12:10
-
@HannaKhalil, Now I'm not sure what your requirements are exactly. Choosing a single bit to set? Choosing any number of bits to set? Something else? Or is it that you have some bits set and you want to choose one of them? – chris May 26 '16 at 12:15
-
The requirement is not clear. Perhaps just select a random bit and then check to see if it is 1? There are [bit twiddling](https://graphics.stanford.edu/~seander/bithacks.html) hacks that can do other tricks that might be useful to you. – wally May 26 '16 at 12:17
-
If it is the last case I mentioned, you could combine the Hamming Weight with an algorithm to get the nth set bit, n being random up to the Hamming Weight. – chris May 26 '16 at 12:19
-
I rephrased it. I need a random index of a 1 bit. for example if I have 0111111000000000000000000000000 so I want to choose randomly 1 or 2 or 3 or 4 or 5 or 6 – Hanna Khalil May 26 '16 at 12:19
-
@chris yes it is the last case. Could you please elaborate more about Hamming weight? – Hanna Khalil May 26 '16 at 12:21
-
The one thing I don't get is "How can I do that without loops and counters". Once you have the number of bits set to 1 (hamming weight as @chris said), just get the nth bit set to 1, n being randomly chosen between 1 and that number. Except that involves a counter *and* a loop. – Nelfeal May 26 '16 at 12:23
-
@Nelxiost, Not if you use a CPU instruction :p In any case, that really depends on your needs and maybe compiler. – chris May 26 '16 at 12:26
-
@HannaKhalil, The Hamming Weight is simply the number of set bits. There are StackOverflow questions containing a variety of ways to get it, but you already have a `std::bitset`, so it's easy, as seen in the answer below. – chris May 26 '16 at 12:28
-
Thank you. I prefer to do it without for loops if it's possible, I thought to use some stl algorithm. Anyway, it seems it's not possible so the solution in the first answer is good for me. – Hanna Khalil May 26 '16 at 12:29
2 Answers
4
Here's a first stab at it:
std::bitset<32> bitset{...};
std::mt19937 prng(std::time(nullptr));
std::uniform_int_distribution<std::size_t> dist{1, bitset.count()};
std::size_t p = 0;
for(std::size_t c = dist(prng); c; ++p)
c -= bitset[p];
// (p - 1) is now the index of the chosen bit.
It works by counting the set bits, doing the random pick c
in that interval, then looking for the c
th set bit.

Quentin
- 62,093
- 7
- 131
- 191
-
-
@HannaKhalil the `for` increment will bump `p` once more after `c` has reached zero, so I needed to offset it. – Quentin May 26 '16 at 13:38
3
If you have 32-bit (or even 64-bit) bitset, more efficient solution would be to convert to integer and then use bitwise operations on that integer to get random set bit.
Here is how you can convert your bitset to unsigned long:
std::bitset<32> word(0x1028);
unsigned long ulWord = word.to_ulong(); // ulWord == 0x1028
Then you can use “Select the bit position“ function from the Bit Twiddling Hacks page to select random set bit efficiently:
unsigned int bitcnt = word.count();
unsigned int randomSetBitIndex = 63-selectBit(ulWord, random() % bitcnt + 1);
unsigned long randomSetBit = 1 << randomSetBitIndex;
Here is the full code:
// Select random set bit from a bitset
#include <iostream>
#include <bitset>
#include <random>
using namespace std;
unsigned int selectBit(unsigned long long v, unsigned int r) {
// Source: https://graphics.stanford.edu/~seander/bithacks.html
// v - Input: value to find position with rank r.
// r - Input: bit's desired rank [1-64].
unsigned int s; // Output: Resulting position of bit with rank r [1-64]
uint64_t a, b, c, d; // Intermediate temporaries for bit count.
unsigned int t; // Bit count temporary.
// Do a normal parallel bit count for a 64-bit integer,
// but store all intermediate steps.
a = v - ((v >> 1) & ~0UL/3);
b = (a & ~0UL/5) + ((a >> 2) & ~0UL/5);
c = (b + (b >> 4)) & ~0UL/0x11;
d = (c + (c >> 8)) & ~0UL/0x101;
t = (d >> 32) + (d >> 48);
// Now do branchless select!
s = 64;
s -= ((t - r) & 256) >> 3; r -= (t & ((t - r) >> 8));
t = (d >> (s - 16)) & 0xff;
s -= ((t - r) & 256) >> 4; r -= (t & ((t - r) >> 8));
t = (c >> (s - 8)) & 0xf;
s -= ((t - r) & 256) >> 5; r -= (t & ((t - r) >> 8));
t = (b >> (s - 4)) & 0x7;
s -= ((t - r) & 256) >> 6; r -= (t & ((t - r) >> 8));
t = (a >> (s - 2)) & 0x3;
s -= ((t - r) & 256) >> 7; r -= (t & ((t - r) >> 8));
t = (v >> (s - 1)) & 0x1;
s -= ((t - r) & 256) >> 8;
return 64-s;
}
int main() {
// Input
std::bitset<32> word(0x1028);
// Initialize random number generator
std::random_device randDevice;
std::mt19937 random(randDevice());
// Select random bit
unsigned long ulWord = word.to_ulong();
unsigned int bitcnt = word.count();
unsigned int randomSetBitIndex = 63-selectBit(ulWord, random() % bitcnt + 1);
unsigned long randomSetBit = 1 << randomSetBitIndex;
// Output
cout << "0x" << std::hex << randomSetBit << endl; // either 0x8, 0x20 or 0x1000
return 0;
}
Run it on Ideone.

Andriy Makukha
- 7,580
- 1
- 38
- 49