在C ++ 11中绘制n个随机值的优雅方法?

时间:2014-07-02 07:05:01

标签: c++ c++11 random

对于我的程序,到目前为止,我需要在[0..k [不时]中使用一个随机值,并使用C ++ 11 <random>功能非常好。我目前的代码类似于

class Random
{
public:
  Random() : rng( rd() ) { }

  inline int getRandNum( int limit ) { return ( numbers(rng) % limit ); } 

private:
  std::random_device rd;
  std::mt19937 rng;
  std::uniform_int_distribution<int> numbers;
};

现在,我需要在[0..k [中]连续绘制 n 不同的值。我在<random>中寻找允许的东西,但是我无法找到它,或者这样的东西还不存在。有没有比调用我的getRandNum函数更聪明,更优雅的方法,并重复直到我得到n个不同的值?

编辑:提出一个想法,在我的程序中k是几千和几十。

3 个答案:

答案 0 :(得分:2)

此解决方案不是C ++特定的,但可以使用任何语言轻松实现。

你想要的基本上是数字0到k,并选择前n个数字,其中n <= k。 这可以使用油藏采样算法来完成。有关伪代码,请参阅此维基百科link

请注意,可以在不存储所有k个数字的情况下获取n个数字并对其进行混洗。也就是说,可以只使用O(n)空间,其中n是您希望获得的随机数的数量,而不是O(k)。如果我们假设生成随机数需要O(1)时间,则该算法的时间复杂度为O(k)。

答案 1 :(得分:1)

如果k是几千且n是十,那么排列生成真的不是最好的选择。但是调用getRandNum也不是你想要的,因为它可以多次返回相同的值。 一种选择是一次生成随机序列,检查数字是否重复。最简单(也可能是最有效)的方法是使用set

像这样:

#include <vector>
#include <set>
#include <iostream>
#include <random>

class Random
{
public:
  Random() : rng( rd() ) { }

  inline int getRandNum( int limit ) { return ( numbers(rng) % limit ); }
  std::set<int> getRandSequence(int limit, int n);

private:
  std::random_device rd;
  std::mt19937 rng;
  std::uniform_int_distribution<int> numbers;
};

std::set<int> Random::getRandSequence(int limit, int n)
{
    std::set<int> generatedSequence;
    while (generatedSequence.size() < n) //size() for set is O(1) if I'm not mistaken
        generatedSequence.insert(getRandNum(limit));
    return generatedSequence;
}

int main()
{
    Random r;
    auto sequence = r.getRandSequence(1000, 10);
    std::cout << "Seq;uence: "  << std::endl;
    for (int number : sequence)
        std::cout << number << std::endl;
    std::cout << "End" << std::endl;

    return 0;
}

Ideone demo.

顺便说一下,random_device创建是昂贵的,但据我记忆,uniform_int_distribution创建并非如此。所以这可能更有效:

std::set<int> Random::getRandSequence(int limit, int n)
{
    std::uniform_int_distribution<int> uiniformDistribution(0, limit);
    std::set<int> generatedSequence;
    while (generatedSequence.size() < n)
        generatedSequence.insert(uiniformDistribution(rng));
    return generatedSequence;
}

此外,当您获得统一分发然后将% limit应用于它时,您将无法再获得统一分发。

答案 2 :(得分:0)

std::random_device rd; // obtain a random number from hardware
std::mt19937 eng(rd()); // seed the generator
std::uniform_int_distribution<> distr(0, 1500); // define the range

for(int a=0; a<limit; a++){
    cout << distr(eng);  //draw random nubmer