【问题标题】:Efficient method for randomly selecting all elements of a std::vector exactly once WITHOUT reshuffling一次随机选择 std::vector 的所有元素而不重新洗牌的有效方法
【发布时间】:2011-12-19 19:26:56
【问题描述】:

我正在寻找一种有效的方法来以随机顺序选择对std::vector<T> 的每个元素的访问,而无需重新洗牌或复制它们,即不使用std::random_shuffle,并确保每个元素仅被选择一次。

我不想复制或重新洗牌,因为 a)T 的每个实例都可能是一个非常大的对象,并且 b)对于我将对向量的元素进行的其他操作,这更容易它们保持相同的顺序。

此外,我真的不想走在不断挑选和拒绝重复的街道上。我很可能会在向量中存储大量此类大型对象,效率是关键,因为我希望每秒多次调用此随机选择方法。

【问题讨论】:

  • 你可以为你的类型实现一个交换方法吗?如果标准库实现使用依赖于参数的查找进行交换(它应该),那么您将得到 O(1) 向量中元素的交换。

标签: c++ random vector std


【解决方案1】:

创建一个与现有向量相同大小的向量,该向量使用指向向量中元素的指针。而是随机打乱指针向量并从那里读取 - 成本低。

【讨论】:

    【解决方案2】:

    您没有告诉我们是要随机迭代整个数组,还是只需要随机的一些元素。

    我假设第一种情况。你需要额外的存储空间来记账,而且无论如何你都需要线性时间来进行洗牌。所以创建一个排列,并保持它的记忆活跃,这样你就可以按照你的意愿重新洗牌。使用 C++11:

    #include <algorithm>
    #include <random>
    #include <numeric>
    
    struct permutation
    {
        permutation(size_t n) 
            : perm(n), g(std::random_device())
        {
            std::iota(perm.begin(), perm.end(), size_t(0));
        }
    
        void shuffle() { std::shuffle(perm.begin(), perm.end(), g); }
    
        size_t operator[](size_t n) const { return perm[n]; }
    
    private:
        std::vector<size_t> perm;
        std::mt19937 g;
    };
    

    用法:

    std::vector<huge_t> v;
    ...
    
    permutation sigma(v.size());
    sigma.shuffle();
    
    const huge_t& x = v[sigma[0]];
    
    ...
    sigma.shuffle(); // No extra allocation
    const huge_t& y = v[sigma[0]];
    

    您可以修改代码以使用 C++03 std::random_shuffle,但请注意随机数生成器的保证很少。

    【讨论】:

    • 不幸的是我使用的是VS2008,我相信它不支持c++11
    • 请注意,std::random_shuffle() 具有接受随机数生成器的重载。如果您愿意,可以传入更高质量的生成器。
    • 只是澄清一下,由于不熟悉c++11,有什么需要适应与C++03一起使用?还是您在更新后已经更改了代码?
    • @AndréCaron:是的,但你会同意正确地做到这一点非常困难,如果可以通过图书馆获得,我不建议这样做。
    • Mersenne twister 的实现有多种在线可用,还有/dev/random et.al。将这些设施包装到一个函数对象中是微不足道的。
    【解决方案3】:

    我认为最简单(也是最有效的解决方案之一)的解决方案是创建一个 std::vector&lt;size_t&gt; 将索引保存到您的 vector&lt;T&gt; 中,或者创建一个 std::vector&lt;T*&gt; 将指针保存到您的 vector 中。然后你可以使用std::random_shuffle 打乱那个,迭代它并从你的原始向量中选择相应的元素。这样你就不会改变你的原始向量的顺序和洗牌指针或size_t 是相当便宜的

    【讨论】:

    • std::vector 方法是我目前实现它的方式,但只是想检查没有更好的方法来执行此操作。休息了几年后又回到了 C++ 编码。
    • +1 基本上相当于w00te的方法,但是和原向量的关系更清晰,原向量可以在两者之间修改,如果你决定的话,在LP64系统上可能内存效率提高50%你很懒,使用int而不是size_t
    • 谢谢大家...... std::vector 方法是我目前实现它的方式,但只是想检查没有更好的方法来做到这一点。经过几年的休息后,我又回到了 C++ 编码。从此....可以说 std::vector ObjVector 和 std::vector index 是否可以使用 for_each (index.begin(), index.end(), someMethod) 来实现以下... someMethod 以某种方式最终调用方法 Obj.method 即 for_each 导致以随机顺序调用 ObjVector 中 Object 的每个实例中的方法?
    • 使用 std::vector 和 std::vector::iterator
    • 哪个更好?存储 std::vector::const_iterator 或 std::vector 作为索引方法?我真的需要存储 std::vector 和 std::vector::iterator 吗?
    猜你喜欢
    • 1970-01-01
    • 2012-08-30
    • 2012-02-07
    • 1970-01-01
    • 2018-02-24
    • 1970-01-01
    • 2011-10-19
    • 2015-01-14
    相关资源
    最近更新 更多