在 O(log(n)) 时间内从 std::set 中随机选择一个元素
Posted
技术标签:
【中文标题】在 O(log(n)) 时间内从 std::set 中随机选择一个元素【英文标题】:Randomly select an element from a std::set in O(log(n)) time 【发布时间】:2018-08-21 15:50:08 【问题描述】:有没有办法从std::set
中的std::set
或O(log(n))
中随机选择一个元素
更好O(1)
时间?它不需要有一个非常均匀的分布,只是一些相当随机的东西
(虽然显然甚至更好)
看着std::set::extract
似乎很有希望
因为它可能会在恒定时间内将set
分成两半,但我找不到
识别靠近根的节点的好方法,我可以使用node_type
文档
find 没有详细说明。
如果一切都失败了,可以将内容复制到std::map
,并使用我认为的随机键
是O(n log(n))
时间,这会让我摊销O(log(n))
时间,但这不是首选的解决方案
因为在我什么都不想要的情况下需要一些开销
【问题讨论】:
您需要使用std::set
吗?如果你想要随机访问,那么你应该使用提供随机访问的容器。
@NathanOliver 是的,std::set
在我的代码的其他地方提供了显着的性能优势,我宁愿不使用不同的容器
std::set
已经提供了O(ln(n))
访问时间。如果你想要O(1)
,那么你应该使用std::unordered_set
。见:***.com/questions/181693/…
【参考方案1】:
如果集合中的元素本身均匀分布在某个值域内,那么您可以在该域内生成一个随机值,并使用std::set::lower_bound
获取集合中包含的第一个不小于随机值。
鉴于您不需要非常均匀的分布,因此集合内元素的均匀性要求可能不是非常必要的。一个元素被选中的可能性取决于它与前一个元素的比较距离。
对于均匀分布,我认为没有比 *std::next(std::begin(s), random_index)
更好的了,它的复杂性是线性的。
对于具有均匀分布和对数渐近复杂度的良好通用解决方案,您需要std::set
以外的其他数据结构。
特别是,一个不错的选择是Order statistic tree,它通过将其子树的大小添加到节点中来扩充搜索树。 OST有一个Select(i)
操作,类似于数组下标操作,同样可以在索引0...N之间随机选取一个元素。
另一种选择是使用排序数组。 sorted 属性可用于保留std::set
具有的对数查找属性。
不幸的是,标准库既没有顺序统计树,也没有排序数组集。
【讨论】:
【参考方案2】:std::set 复杂度是 o(log(n)) 所以你不能单独使用 std::set 来降低搜索复杂度。您可以使用像向量这样的索引结构来实现这一点。
此外,您无法使用此代码实现随机搜索:
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis( 0, set::size ( ) );
然后
set::operator [] (dis(gen));
或其他的东西
【讨论】:
以上是关于在 O(log(n)) 时间内从 std::set 中随机选择一个元素的主要内容,如果未能解决你的问题,请参考以下文章
leetcode题解之Find the Duplicate Number
Codility峰的O(N * log(log(N)))算法?