我这样定义一个unordered_map:
unordered_map
std::unordered_map<std::string, Edge> edges;
有没有一种有效的方法可以从无序Map的边中选择一个随机边?
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函数。
edges
[0 ; edges.size() - 1]
std::next
std::advance
zf9nrax12#
有没有一种有效的方法可以从无序Map的边中选择一个随机边?如果你所说的有效是指O(1),那么不,它是不可能的。因为unordered_map::begin / end返回的迭代器是ForwardIterator s,所以简单使用std::advance的方法在元素数量上是O(n)。如果您的特定用途允许,您可以用一些随机性来换取效率:你可以选择一个随机的 bucket(可以在O(1)中访问),然后在这个bucket中选择一个随机元素。
unordered_map::begin / end
ForwardIterator
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中的元素一些特权。
rnd(n)
7ajki6be3#
严格O(1)解:1.保持一个密钥向量,当你需要从你的Map中获取一个随机元素时,从向量中选择一个随机密钥,然后从Map中返回相应的值-需要恒定的时间1.如果您将一个键-值对插入到Map中,请检查这样的键是否已经存在,如果不存在,请将该键添加到您的键向量中-这需要恒定的时间1.如果你想在一个元素被选中后将其从Map中移除,可以将选中的键与键向量的back()元素交换,然后调用pop_back(),然后从Map中擦除该元素并返回值-需要恒定的时间但是,有一个限制:如果你想从map中删除元素,除了随机选取之外,你需要修改你的密钥向量,这需要O(n),但是仍然有一种方法可以获得O(1)的性能:保留一个Map,告诉你键在键向量中的位置,并用swap更新它:)
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()) );
agyaoht75#
的解
是极其缓慢的...更快的解决方案是:
std::vector<std::string> vec
0
vec.size() - 1
int index
edges[vec[index]]
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(); */
6条答案
按热度按时间qf9go6mv1#
这不是一个O(1)解(除非你只得到一条边):
C++11之前的解决方案:
C++11后续解决方案:
选择有效随机数的函数取决于您的选择,但当
edges
不为空时,请确保它返回[0 ; edges.size() - 1]
范围内的数字。std::next
函数只是以允许直接赋值的方式 Packagestd::advance
函数。zf9nrax12#
有没有一种有效的方法可以从无序Map的边中选择一个随机边?
如果你所说的有效是指O(1),那么不,它是不可能的。
因为
unordered_map::begin / end
返回的迭代器是ForwardIterator
s,所以简单使用std::advance
的方法在元素数量上是O(n)。如果您的特定用途允许,您可以用一些随机性来换取效率:
你可以选择一个随机的 bucket(可以在O(1)中访问),然后在这个bucket中选择一个随机元素。
其中
rnd(n)
返回[0,n)范围内的随机数。在实践中,如果你有一个不错的散列,那么大多数的bucket将只包含一个元素,否则这个函数会稍微给那些单独在bucket中的元素一些特权。
7ajki6be3#
严格O(1)解:
1.保持一个密钥向量,当你需要从你的Map中获取一个随机元素时,从向量中选择一个随机密钥,然后从Map中返回相应的值-需要恒定的时间
1.如果您将一个键-值对插入到Map中,请检查这样的键是否已经存在,如果不存在,请将该键添加到您的键向量中-这需要恒定的时间
1.如果你想在一个元素被选中后将其从Map中移除,可以将选中的键与键向量的back()元素交换,然后调用pop_back(),然后从Map中擦除该元素并返回值-需要恒定的时间
但是,有一个限制:如果你想从map中删除元素,除了随机选取之外,你需要修改你的密钥向量,这需要O(n),但是仍然有一种方法可以获得O(1)的性能:保留一个Map,告诉你键在键向量中的位置,并用swap更新它:)
xzv2uavs4#
下面是从Map中获取随机元素的方法:
或者看看this answer,它提供了以下解决方案:
agyaoht75#
的解
是极其缓慢的...
更快的解决方案是:
std::vector<std::string> vec
0
到vec.size() - 1
的随机int index
edges[vec[index]]
rqmkfv5c6#
你可以看到这个问题:
problem 380. Insert Delete GetRandom O(1)你可以构建一个向量来使用向量随机迭代器,更有效地得到随机值。