How can I select a random element in an std::set
?
I naively tried this:
int GetSample(const std::set& s) {
double r =
If the random access is important and you can live with O(N) average effort for the insertion, then the workaround given in this paper might be convenient.
The main idea there is to use a sorted vector, and then for lookup the function std::lower_bound
. This, the lookup takes O(log N) just as in a normal set. Further, (random) insertion takes O(N), as all following elements must be shifted just like in a normal vector (and possibly a reallocation is performed). Insertion at the back, however, is constant (except for the reallocation. You can avoid this by calling reserve()
with a large enough storage).
Finally, the main point of the question: Random access is O(1). Just draw a random number i
from a uniform distribution in [0, V.size()-1]
, and return the corresponding element V[i]
.
Here is the code basis out of the paper, which implements this sorted vector. Extend it as needed:
template >
struct sorted_vector {
using std::vector;
using std::lower_bound;
vector V;
Compare cmp;
typedef typename vector::iterator iterator;
typedef typename vector::const_iterator const_iterator;
iterator begin() { return V.begin(); }
iterator end() { return V.end(); }
const_iterator begin() const { return V.begin(); }
const_iterator end() const { return V.end(); }
//...if needed, implement more by yourself
sorted_vector(const Compare& c = Compare()) : V(), cmp(c) {}
template
sorted_vector(InputIterator first, InputIterator last, Const Compare& c = Compare())
: V(first, last), cmp(c)
{
std::sort(begin(), end(), cmp);
}
//...
iterator insert(const T& t) {
iterator i = lower_bound(begin(), end(), t, cmp);
if (i == end() || cmp(t, *i))
V.insert(i, t);
return i;
}
const_iterator find(const T& t) const {
const_iterator i = lower_bound(begin(), end(), t, cmp);
return i == end() || cmp(t, *i) ? end() : i;
}
};
For a more sophisticated implementation, you might also consider this page.
EDIT: or even better, use boost::container::flat_set
, which implements the set using the idea above, i.e. as a sorted vector.