How to select a random element in std::set in less than O(n) time?

大城市里の小女人 提交于 2019-11-30 11:41:48

Introduce array with size equal to set. Make array elements hold addresses of every element in set. Generate random integer R bounded by array/set size, pick address in array's element indexed by R and dereference it to obtain set's element.

I don't see how to do it with just std::set, so you probably need a different data structure. Like Victor Sorokin said, you can combine a set with a vector. Instead of set<T>, use map<T, size_t>, plus vector< map<T, size_t>::iterator >. The value for each key is an index into the vector, and each element of the vector points back to the map element. The vector elements have no particular order. When you add an element, put it at the end of the vector. When you remove an element and it's not the last one in the vector, move the last element to the deleted element's position.

IF you know the distribution of the elements in the set, you can randomly select key (with that same distribution) and use std::set::lower_bound. That's a lot of if though.

int main() {
    std::set<float> container;
    for(float i=0; i<100; i += .01)  
        container.insert(i);
    //evenish distribution of 10000 floats between 0 and 100.
    float key = std::rand() *10000f / RAND_MAX; //not random, sue me
    std::set<float>::iterator iter = container.lower_bound(key); //log(n)
    std::cout << *iter;
    return 0;
}

You may be able to make a randomly-ordered copy of the map by using this constructor

template <class InputIterator>
set(InputIterator f, InputIterator l,
    const key_compare& comp)

..and passing a comparator that compares hashes of the keys (or some other deterministic spreading function.) Then take the "smallest" keys according to this new map.

You could construct the map once and amortize the cost across several requests for a "random" element.

For std::unordered_set<int> s:

1) take random R in min(s)..max(s)

2) if R in s: return R

3)

newIter = s.insert(R).first;
newIter++;
if (newIter == s.end()) {
    newIter = s.begin();
}
auto result = *newIter;
s.erase(R);
return result;

For ordered set (std::set) probability would depend on distance between elements. unordered_set is randomized by hash.

I hope this can help.

PS converting std::set<V> into std::set<std::pair<int, V>> (where first element in pair is a hash of second) makes this method suitable for any hashable V.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!