有没有办法在O(1)平均时间内从C ++ unordered_set 中随机检索一个元素?而不是做
std::unordered_set<int> s;
// initialize s
auto start = s.begin();
for (int i = 0; i < rand()%s.size()-1; ++i, ++start) {}
int randomNumber = *start;
更新
我需要争取帖子,所以我添加了我需要上述功能的原因。
我正在玩实施迷宫发生器。不知何故,我需要一个支持的数据结构:
std :: vector 具有随机访问权限,但插入/删除费用很高
std :: list 没有随机访问
std :: set 支持O(logN)随机访问和O(logN)插入/删除,这很棒,但我的初始化是一个排序的序列,很容易打破它的平衡
所以我认为哈希表是最好的选择,但随机检索一个元素将是非常重要的。
感谢您的时间。
答案 0 :(得分:2)
您无法在O(1)时间内从unordered_set
中选择随机元素。迭代器是ForwardIterator
s,而不是RandomAccessIterator
s。您必须使用不同的容器。要么boost::container::flat_set<int>
,要么自己编写内容类似vector
的内容:
template <typename T>
class set_with_random_access
{
std::vector<T*> vec;
std::unordered_set<T> set;
};
我们为其提供保持这些功能的功能,例如插入:
void insert(const T& value) {
auto pr = set.insert(value);
if (pr.second) {
vec.push_back(&*pr.first);
}
}
随机性:
template <typename GEN>
T& random(GEN& gen) {
std::uniform_int_distribution<size_t> dist(0, vec.size() - 1);
return *vec[dist(gen)];
}
坦率地说,这是很多工作,所以可能会使用boost。
答案 1 :(得分:2)
在O(1)平均时间内从C ++
unordered_set
中随机检索元素的方法吗?
取决于&#34;随机&#34;为了你的目的,是否是一个小于O(1)的小微笑是足够好的。你可以选择一个随机的桶#34; b
&#34;在0
和s.bucket_count() - 1
之间,如果存储桶为空,则重复,然后在li
和0
之间重复列表索引s.bucket_size(b) - 1
,然后{{1}获取一个&#34;随机的迭代器&#34;但是,请考虑这种情况:
你滚动三个骰子 - 然后随机选择其中一个:你得到一个随机的1-6值,偶数概率,但如果你继续采摘而不再滚动你只能得到任何价值最终三个骰子:从1到6的每个值的概率严重不均匀。
在std::advance(s.begin(li))
中选择随机元素的上述方法有点像:如果有unordered_set
桶有元素,那么每个桶甚至有机会被选中,但元素在该桶中有x
个选择的机会 - 对于任何给定的桶 - 可能小于或大于1 / x / bucket_size()
。换句话说,如果你认为散列是有效随机的,那么各种元素在它们的位置上都有相同的机会受到青睐或受到惩罚,但是这种情况有“歪斜”。然后设置它直到表格数据显着变异或表格重新出现(并且如果它通过加倍表格大小而不是更大的素数而重新表达(模糊的记忆{{{ 1}}双打),然后一次惩罚的值将倾向于在一半的时间内受到惩罚)。
以上的大O效率是O(1)以上的微小因素,因为:
在初始探测中有一些重复,要找到一个带元素的存储桶,但加载因子为1.0,它不太可能需要多次尝试(给定一个好的散列函数) );其他选项是可用的 - 比如从空桶中迭代,或者通过各种位移跳跃(修改为表格大小) - 这可能比尝试另一个完全随机的桶更好一点,但也可能加剧元素选择的可能性差异
在任何给定存储桶中碰撞的元素中存在线性迭代,但由于默认负载因子为1.0,因此很少会发生多次碰撞,并且越来越少见还有更多。
答案 2 :(得分:1)
从std::unordered_set
中挑选一个随机元素是一个坏主意。这是因为std::unordered_set
不支持随机访问,因此没有下标运算符(即operator[]
)。
我坚信您需要的是std::vector
并结合std::unique
以满足元素的唯一性。
在下面的示例中,我使用std::vector
,然后通过在其上应用std::unique
算法确保它只有唯一的元素。然后我使用random
实用程序来生成[0,向量的大小 - 1]中的随机索引:
std::vector<int> v{1, 2, 8, 3, 5, 4, 5, 6, 7, 7, 9, 9, 19, 19};
v.erase(std::unique(v.begin(), v.end()), v.end());
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(0, v.size() - 1);
std::cout << "Random number from vector: " << v[distribution(generator)] << std::endl;