How can I select a random element in an std::set
?
I naively tried this:
int GetSample(const std::set<int>& s) { double r = rand() % s.size(); return *(s.begin() + r); // compile error }
But the operator+
is not allowed in this way.
In fact, if you are thinking of uses of random-access iterators, then you can use random-access iterator in place of any other type of iterator since it is the strongest and the best type of iterator available in the C++ Standard library.
In order to get random elements from the HashSet object, we need to generate a random number between 0 (inclusive) and the size of the HashSet (exclusive). And then iterate through the set till we reach the element located at the random number position as given below.
To generate the random values between 0 and n-1 , we can use the cstdlib's functions rand() and srand() or use any of the standard generators defined in the <random> header introduced in C++11.
You could use the std::advance
method.
#include <set> #include <algorithm> int main() { using namespace std; // generate a set... set<int> s; for( int i = 0; i != 10; ++i ) s.insert(i); auto r = rand() % s.size(); // not _really_ random auto n = *select_random(s, r); }
Where
template<typename S> auto select_random(const S &s, size_t n) { auto it = std::begin(s); // 'advance' the iterator n times std::advance(it,n); return it; }
A hypothesized in a comment above, it can be done in O(log(n)) (vs O(n) for std::advance
) without a vector (using O(n) more space) by using the method I describe here.
Essentially, you :
it
on it*(it++)
or *(set.begin())
if it
at the endn.b : As pointed out by Aaron the element is not chosen uniformly at random. You need to build the random element with the same distribution than the elements in the set to approach a uniform polling.
davidhigh already gave the solution with a vector but there is a problem because when you pop an element of your stack, you will have to perform a linear search in O(n) or you can rebuild your vector each time you want to retrieve a random element but that is O(n) too.
To avoid this problem and keep the insert/delete to O(log n), you can keep an std::unordered_set
and use a similar method to the first solution to get a random element in O(1).
p.s : If your elements are large you can use an unordered set of pointers (with a modified hasher) to spare some memory.
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