在unordered_map中选择随机元素

Select random element in an unordered_map

本文关键字:选择 随机 元素 map unordered      更新时间:2023-10-16

我定义了一个这样的unordered_map

std::unordered_map<std::string, Edge> edges;

有没有一种有效的方法可以从unordered_map边中选择随机边?

这不是

O(1) 解决方案(除非你只有一个边):

C++11 之前的解决方案:

std::tr1::unordered_map<std::string, Edge> edges;
std::tr1::unordered_map<std::string, Edge>::iterator random_it = edges.begin();
std::advance(random_it, rand_between(0, edges.size()));

C++11 起解决方案:

std::unordered_map<std::string, Edge> edges;
auto random_it = std::next(std::begin(edges), rand_between(0, edges.size()));
选择

有效随机数的函数由您选择,但请确保它在edges不为空时返回范围内[0 ; edges.size() - 1]范围内的数字。

std::next函数只是以允许直接赋值的方式包装std::advance函数。

有没有一种有效的方法可以从unordered_map边中选择随机边?

如果你所说的效率是指O(1),那么不,这是不可能的。

由于unordered_map::begin / end返回的迭代器是ForwardIterator s,因此仅使用std::advance的方法在元素数量上是O(n)。

如果你的特定用途允许,你可以用一些随机性来换取效率:

您可以选择一个随机存储桶(可以在 O(1) 中访问),然后选择该存储桶内的随机元素。

int bucket, bucket_size;
do
{ 
    bucket = rnd(edges.bucket_count());
}
while ( (bucket_size = edges.bucket_size(bucket)) == 0 );
auto element = std::next(edges.begin(bucket), rnd(bucket_size));

其中rnd(n)返回 [0,n) 范围内的随机数。

实际上,如果你有一个不错的哈希值,大多数桶将只包含一个元素,否则这个函数将稍微特权那些单独存在于其桶中的元素。

没有存储桶的严格 O(1) 解决方案:

    保留一个键向量,
  1. 当你需要从地图中获取一个随机元素时,从向量中选择一个随机键并从地图中返回相应的值 - 需要恒定时间
  2. 如果您在映射中插入键值对,请检查该键是否已经存在,如果不是这种情况,请将该键添加到您的键向量中 - 需要恒定时间
  3. 如果要在选择元素后从映射中删除该元素,请将您选择的键与键向量的 back() 元素交换并调用 pop_back),然后从映射中删除该元素并返回值 - 需要恒定时间

但是,有一个限制:如果你想从地图中删除元素而不是随机选择,你需要修复你的键向量,这需要 O(n) 和幼稚的方法。但是仍然有一种方法可以获得 O(1) 性能:保留一个地图,告诉您密钥在密钥向量中的位置,并使用交换:)对其进行更新

这是从地图中获取随机元素的方法:

std::unordered_map<std::string, Edge> edges;
iterator item = edges.begin();
int random_index = rand() % edges.size();
std::advance(item, random_index);

或者看看这个答案,它提供了以下解决方案:

std::unordered_map<std::string, Edge> edges;
iterator item = edges.begin();
std::advance( item, random_0_to_n(edges.size()) );

的解决方案

std::unordered_map<std::string, Edge> edges;
auto random_it = std::next(std::begin(edges), rand_between(0, edges.size()));

非常慢。

一个更快的解决方案是:

  • 指定边时,将其关键帧同时放置到std::vector<std::string> vec
  • 随机int index范围从0vec.size() - 1
  • 然后得到edges[vec[index]]

你可以看到这个问题:

问题 380.插入删除 获取随机 O(1)您可以构建一个向量来使用向量随机迭代器,更有效地获取随机值。喜欢这个:

    class RandomizedSet {
public:
    unordered_map<int, int> m;
    vector<int> data;
    RandomizedSet() {
    }
    
    bool insert(int val) {
        if(m.count(val)){
            return false;
        } else{
            int index = data.size();
            data.push_back(val);
            m[val] = index;
            return true;
        }
    }
    
    bool remove(int val) {
        if(m.count(val)){
            int curr_index = m[val];
            int max_index = data.size()-1;
            m[data[max_index]] = curr_index;
            swap(data[curr_index], data[max_index]);
            data.pop_back();
            m.erase(val);
            return true;
        } else{
            return false;
        }
    }
    
    int getRandom() {
        return data[rand() % data.size()];
    }
};
/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet* obj = new RandomizedSet();
 * bool param_1 = obj->insert(val);
 * bool param_2 = obj->remove(val);
 * int param_3 = obj->getRandom();
 */