How to select a random element in std::set?

后端 未结 6 1338
北荒
北荒 2020-11-30 09:26

How can I select a random element in an std::set?

I naively tried this:

int GetSample(const std::set& s) {
  double r =          


        
6条回答
  •  忘掉有多难
    2020-11-30 10:03

    First Solution : O(log n) in time / O(1) in space (not uniform !)

    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 :

    • check if the set is empty (if it is, there is no hope)
    • generate a random value
    • if already there return it else insert it
    • get one iterator it on it
    • get the random element as *(it++) or *(set.begin()) if it at the end
    • return it not before deleting the element you inserted

    n.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.

    Second Solution : O(1) in time / O(n) in space (uniform)

    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.

提交回复
热议问题