【问题标题】:How to select a random element in std::set in less than O(n) time?如何在少于 O(n) 的时间内在 std::set 中选择一个随机元素?
【发布时间】:2012-01-08 06:56:26
【问题描述】:

This question 添加了约束。

我愿意允许不统一的选择,只要不偏向一边。

鉴于“sets are typically implemented as binary search trees”并且我希望它们包含某种深度或大小信息以进行平衡,我希望您可以对树进行某种加权随机游走。但是我不知道有任何远程便携的方式来做到这一点。

编辑:约束不适用于摊销时间。

【问题讨论】:

  • 现在这是一个有趣的问题,但 会将它作为平衡树的一个特性来实现,这对于库实现来说很难做到。
  • std::set 未定义为 bstree。它的复杂性要求本质上意味着它不能是其他任何东西,但树结构不是标准的一部分,因此也不是接口的一部分。 (如果你有一个实际的平衡树,你可以在 O(log n) 中随机选择一个元素,方法是随机选择左子或右子,直到你在底部。)也许应该为下一个建议一个 random() 接口标准;毕竟,已经有了random_shuffle 算法,这也不例外。 (顺便说一句,您可以在 O(1) 中对 std::unordered_set 进行操作。)
  • @KerrekSB:也许选择左、右或停止,所以至少每个元素都有机会。
  • @GMan:是的,当然,谢谢!必须相应地调整概率。
  • 看来我得等 C++2x 中的random() 接口了。

标签: c++ set stl-algorithm


【解决方案1】:

您可以使用此构造函数制作随机排序的地图副本

template <class InputIterator>
set(InputIterator f, InputIterator l,
    const key_compare& comp)

..并传递一个比较键的哈希值的比较器(或其他确定性扩展函数)。然后根据这个新映射获取“最小”键。

您可以构建一次地图,并在多个“随机”元素的请求中分摊成本。

【讨论】:

  • 这是严格的弱排序吗?
  • 但那是 O(n),不是吗?所以它相当优雅地解决了另一个问题,但没有解决这个问题。
  • 通过在调用间摊销构造,隐藏常量可以任意小。
  • @KerrekSB 不,不是。可以改为比较键的哈希值,更新。
  • 这比 O(n) 更糟糕,除非 std::set 是一个哈希表。
【解决方案2】:

引入大小等于集合的数组。使数组元素保存集合中每个元素的地址。生成以数组/集合大小为边界的随机整数R,在以R 为索引的数组元素中选择地址并取消引用以获取集合的元素。

【讨论】:

  • 并在每次集合改变时重新生成这个数组。
  • 是的。 OP 帖子中的任何地方都没有说设置会发生变化。此外,没有任何地方说有任何内存限制:)
  • 这也是真的 :) 您的解决方案看起来是唯一可以通过该集合的公共界面实现的解决方案。
  • 嗯,不知何故,这感觉像是在作弊……“将集合复制到不同的数据结构中,然后做其他事情”……我的意思是,它有效,但感觉好像没有抓住重点.
  • 我正在寻找一种无论多久修改一次都保持有效的解决方案。
【解决方案3】:

如果你知道集合中元素的分布,你可以随机选择键(具有相同的分布)并使用std::set::lower_bound。不过,这有很多。

int main() {
    std::set<float> container;
    for(float i=0; i<100; i += .01)  
        container.insert(i);
    //evenish distribution of 10000 floats between 0 and 100.
    float key = std::rand() *10000f / RAND_MAX; //not random, sue me
    std::set<float>::iterator iter = container.lower_bound(key); //log(n)
    std::cout << *iter;
    return 0;
}

【讨论】:

  • @BCS:我认为除此之外的标准集无法在 logN 中完成,您必须自己动手。
【解决方案4】:

我不知道如何仅使用std::set 来完成此操作,因此您可能需要不同的数据结构。就像 Victor Sorokin 说的,你可以把一个集合和一个向量结合起来。代替set&lt;T&gt;,使用map&lt;T, size_t&gt;,加上vector&lt; map&lt;T, size_t&gt;::iterator &gt;。每个键的值是向量的索引,向量的每个元素都指向映射元素。向量元素没有特定的顺序。添加元素时,请将其放在向量的末尾。当您删除一个元素并且它不是向量中的最后一个元素时,将最后一个元素移动到已删除元素的位置。

【讨论】:

    【解决方案5】:

    对于std::unordered_set&lt;int&gt; s

    1) 在min(s)..max(s)中随机抽取R

    2) 如果Rs 中:返回R

    3)

    newIter = s.insert(R).first;
    newIter++;
    if (newIter == s.end()) {
        newIter = s.begin();
    }
    auto result = *newIter;
    s.erase(R);
    return result;
    

    对于有序集 (std::set),概率取决于元素之间的距离。 unordered_set 通过哈希随机化。

    我希望这会有所帮助。

    PS 将 std::set&lt;V&gt; 转换为 std::set&lt;std::pair&lt;int, V&gt;&gt;(其中第一个元素是第二个的哈希)使此方法适用于任何可哈希的 V。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 2012-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-26
      • 1970-01-01
      相关资源
      最近更新 更多