c++ 选择unordered_map中的随机元素

mcvgt66p  于 2023-03-14  发布在  其他
关注(0)|答案(6)|浏览(255)

我这样定义一个unordered_map

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

有没有一种有效的方法可以从无序Map的边中选择一个随机边?

qf9go6mv

qf9go6mv1#

这不是一个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函数只是以允许直接赋值的方式 Package std::advance函数。

zf9nrax1

zf9nrax12#

有没有一种有效的方法可以从无序Map的边中选择一个随机边?
如果你所说的有效是指O(1),那么不,它是不可能的。
因为unordered_map::begin / end返回的迭代器是ForwardIterator s,所以简单使用std::advance的方法在元素数量上是O(n)。
如果您的特定用途允许,您可以用一些随机性来换取效率:
你可以选择一个随机的 bucket(可以在O(1)中访问),然后在这个bucket中选择一个随机元素。

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)范围内的随机数。
在实践中,如果你有一个不错的散列,那么大多数的bucket将只包含一个元素,否则这个函数会稍微给那些单独在bucket中的元素一些特权。

7ajki6be

7ajki6be3#

严格O(1)解:
1.保持一个密钥向量,当你需要从你的Map中获取一个随机元素时,从向量中选择一个随机密钥,然后从Map中返回相应的值-需要恒定的时间
1.如果您将一个键-值对插入到Map中,请检查这样的键是否已经存在,如果不存在,请将该键添加到您的键向量中-这需要恒定的时间
1.如果你想在一个元素被选中后将其从Map中移除,可以将选中的键与键向量的back()元素交换,然后调用pop_back(),然后从Map中擦除该元素并返回值-需要恒定的时间
但是,有一个限制:如果你想从map中删除元素,除了随机选取之外,你需要修改你的密钥向量,这需要O(n),但是仍然有一种方法可以获得O(1)的性能:保留一个Map,告诉你键在键向量中的位置,并用swap更新它:)

xzv2uavs

xzv2uavs4#

下面是从Map中获取随机元素的方法:

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

或者看看this answer,它提供了以下解决方案:

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

agyaoht75#

的解

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
  • 0vec.size() - 1的随机int index
  • 然后得到edges[vec[index]]
rqmkfv5c

rqmkfv5c6#

你可以看到这个问题:
problem 380. Insert Delete GetRandom 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();
 */

相关问题