如何在C ++中生成随机数?

时间:2012-11-18 23:23:09

标签: c++ random

我正在尝试用骰子制作游戏,我需要在其中有随机数字(模拟骰子的两侧。我知道如何在1到6之间进行)。使用

#include <cstdlib> 
#include <ctime> 
#include <iostream>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

效果不好,因为当我运行程序几次时,这是我得到的输出:

6
1
1
1
1
1
2
2
2
2
5
2

所以我想要一个每次生成不同随机数的命令,而不是连续5次生成随机数。是否有命令可以做到这一点?

12 个答案:

答案 0 :(得分:132)

使用modulo可能会在随机数中引入偏差,具体取决于随机数生成器。 See this question for more info.当然,完全有可能以随机顺序重复数字。

尝试一些C ++ 11功能以便更好地分发:

#include <random>
#include <iostream>

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]

    std::cout << dist6(rng) << std::endl;
}

See this question/answer for more info on C++11 random numbers.以上不是唯一的方法,但这是一种方式。

答案 1 :(得分:49)

您的测试应用程序最基本的问题是,您只需拨打srand一次,然后再拨打rand并退出。

srand函数的重点是用随机种子初始化伪随机数序列。这意味着如果您在两个不同的应用程序中将相同的值传递给srand(使用相同的srand / rand实现),您将获得完全相同的rand()值序列之后阅读。但是你的伪随机序列只包含一个元素 - 你的输出由不同的伪随机序列的第一个元素组成,这些元素以1秒精度的时间播种。那你期望看到什么?当您碰巧在同一秒运行应用程序时,您的结果当然是相同的(正如Martin York在答案评论中已提到的那样)。

实际上,您应该拨打srand(seed)一次,然后多次拨打rand()并分析该序列 - 它应该是随机的。

答案 2 :(得分:9)

如果您使用boost库,则可以通过以下方式获取随机生成器:

#include <iostream>
#include <string>

// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using namespace std;
using namespace boost;

int current_time_nanoseconds(){
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_nsec;
}

int main (int argc, char* argv[]) {
    unsigned int dice_rolls = 12;
    random::mt19937 rng(current_time_nanoseconds());
    random::uniform_int_distribution<> six(1,6);

    for(unsigned int i=0; i<dice_rolls; i++){
        cout << six(rng) << endl;
    }
}

函数current_time_nanoseconds()给出当前时间(以纳秒为单位),用作种子。


这是一个更通用的类,用于获取范围内的随机整数和日期:

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer {
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() {
        if(debug_mode==true){
            this->rng_ = random::mt19937();
        }else{
            this->rng_ = random::mt19937(current_time_nanoseconds());
        }
    };

    int current_time_nanoseconds(){
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    }

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance(){
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    }
    bool method() { return true; };

    int rand(unsigned int floor, unsigned int ceil){
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    }

    // Is not considering the millisecons
    time_duration rand_time_duration(){
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    }


    date rand_date_from_epoch_to_now(){
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    }

    date rand_date_from_epoch_to_ceil(date ceil_date){
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    }

    date rand_date_in_interval(date floor_date, date ceil_date){
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    }

    ptime rand_ptime_from_epoch_to_now(){
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    }

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    }

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    }
};

答案 3 :(得分:7)

#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand(time(NULL));
    int random_number = std::rand(); // rand() return a number between ​0​ and RAND_MAX
    std::cout << random_number;
    return 0;
}

http://en.cppreference.com/w/cpp/numeric/random/rand

答案 4 :(得分:7)

每当您使用C ++编程语言对random number generation进行基本的网络搜索时,通常都会首先出现此问题!我想大声疾呼,以期希望更好地阐明C ++中伪随机数生成的概念,以便将来的程序员不可避免地会在网上搜索相同的问题!

基础

伪随机数生成涉及利用deterministic algorithm生成数字序列的过程,该序列的性质近似于随机数。我说近似,因为真正的随机性在数学和计算机科学中相当elusive mystery。因此,为什么使用伪随机一词在学究上更正确!

在实际使用PRNG即pseudo-random number generator之前,必须为算法提供一个初始值,通常也称为 seed 。但是,只能使用算法本身一次 设置种子!

/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
  PRNG( seed ) /// Will work as expected

/// Wrong way!
for( x in rang( 0, 10 ) ):
  seed( 1234 ) /// Seed reset for ten iterations!
  PRNG( seed ) /// Output will be the same...

因此,如果您想要一个好的数字序列,则必须为PRNG提供充足的种子!

旧的C语言方式

C ++具有的C的向后兼容标准库使用在cstdlib头文件中找到的称为linear congruential generator的东西!该PRNG通过利用模块化算术的不连续分段函数来起作用,即,喜欢使用modulo operator '%'的快速算法。关于@Predictability提出的原始问题,以下是此PRNG的常用用法:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main( void )
{
  int low_dist  = 1;
  int high_dist = 6;
  std::srand( ( unsigned int )std::time( nullptr ) );
  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
  return 0;
}

C的PRNG的常用用法包含很多问题,例如:

  1. 对于在给定范围之间正确生成伪随机数,std::rand()的整个界面不是很直观,例如,以@Predictability所需的方式在[1,6]之间生成数字。
  2. 由于Pigeonhole Principlestd::rand()的常用用法消除了伪随机数均匀分布的可能性。
  3. 从技术上讲,std::rand()是通过std::srand( ( unsigned int )std::time( nullptr ) )播种的常见方法是不正确的,因为time_t被认为是restricted type。因此,不能保证从time_tunsigned int的转换

有关使用C的PRNG的总体问题以及如何规避它们的更多详细信息,请参阅Using rand() (C/C++): Advice for the C standard library’s rand() function

标准C ++方式

自从ISO / IEC 14882:2011标准(即C ++ 11)发布以来,random库已经脱离C ++编程语言已有一段时间了。该库配备了多个 PRNG,以及不同的发行类型,例如:uniform distributionnormal distributionbinomial distribution等。以下源代码示例演示了random库的非常基本的用法,涉及@Predictability的原始问题:

#include <iostream>
#include <cctype>
#include <random>

using u32    = uint_least32_t; 
using engine = std::mt19937;

int main( void )
{
  std::random_device os_seed;
  const u32 seed = os_seed();

  engine generator( seed );
  std::uniform_int_distribution< u32 > distribute( 1, 6 );

  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << distribute( generator ) << std::endl;
  return 0;
}

在上面的示例中,使用了整数值的均匀分布的32位Mersenne Twister引擎。 (源代码中的引擎名称听起来很奇怪,因为它的名称来自其period的2 ^ 19937-1)。该示例还使用std::random_device来播种引擎,该引擎从操作系统获取其值(如果您使用的是Linux系统,那么std::random_device将从/dev/urandom返回一个值)。

请注意,您不必使用std::random_device来播种任何引擎。您可以使用 constants 甚至是chrono库!您也不必使用std::mt19937引擎的32位版本,还有其他选项!有关random库功能的更多信息,请参考cplusplus.com

总而言之,C ++程序员不应该再使用std::rand()了,不是因为它的,而是因为当前的标准提供了更好的替代方法,这些替代方法更容易实现。 >和 reliable 。希望你们中的许多人发现这有帮助,尤其是最近通过网络搜索generating random numbers in c++的人!

答案 5 :(得分:2)

Can get full Randomer class code for generating random numbers from here!

如果在项目的不同部分需要随机数,则可以创建一个单独的类Randomer来封装其中的所有random内容。

类似的东西:

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */ 

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

这样的课程以后会很方便:

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}

您可以检查this link as an example how i use这样的Randomer类以生成随机字符串。您也可以根据需要使用Randomer

答案 6 :(得分:2)

我知道如何在不使用任何头文件、编译器内部函数或其他任何东西的情况下,在 C++ 中生成随机数。

#include <cstdio> // Just for printf
int main() {
    auto val = new char[0x10000];
    auto num = reinterpret_cast<unsigned long long>(val);
    delete[] val;
    num = num / 0x1000 % 10;
    printf("%llu\n", num);
}

运行一段时间后,我得到以下统计数据:

0: 5268
1: 5284
2: 5279
3: 5242
4: 5191
5: 5135
6: 5183
7: 5236
8: 5372
9: 5343

看起来很随意。

工作原理:

  • 现代编译器使用 ASLR(地址空间布局随机化)保护您免于缓冲区溢出。
  • 因此您可以在不使用任何库的情况下生成一些随机数,但这只是为了好玩。不要那样使用 ASLR。

答案 7 :(得分:1)

这是一个解决方案。创建一个返回随机数并放置它的函数 在主要功能之外使其全球化。希望这会有所帮助

#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
    srand((unsigned)time(0));
    int die1;
    int die2;
    for (int n=10; n>0; n--){
    die1 = rollDie();
    die2 = rollDie();
    cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
    return (rand()%6)+1;
}

答案 8 :(得分:1)

随机每个RUN文件

size_t randomGenerator(size_t min, size_t max) {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);

    return dist(rng);
}

答案 9 :(得分:1)

  

每次生成一个不同的随机数,而不是连续六次生成相同的随机数。

用例场景

我把可预测性的问题比作一包六张纸,每张纸上写着0到5的值。每次需要新值时,从袋子中抽出一张纸。如果袋子是空的,那么这些号码会被放回袋子里。

...从这里,我可以创建一种各种算法。

<强>算法

行李通常是Collection。我选择了bool[](也称为布尔数组,位平面或位图)来扮演角色。

我选择bool[]的原因是因为每个项目的索引已经是每张纸的价值。如果论文要求在其上写下任何其他内容,那么我会在其位置使用Dictionary<string, bool>。布尔值用于跟踪数字是否已被绘制。

名为RemainingNumberCount的计数器初始化为5,该计数器会在选择随机数时倒计时。这使我们无需计算每次我们希望绘制新数字时剩余的纸张数量。

要选择下一个随机值,我使用for..loop来浏览索引包,并选择一个计数器,以便在index false调用时进行计数{ {1}}。

NumberOfMoves用于选择下一个可用号码。 NumberOfMoves首先设置为NumberOfMoves0之间的随机值,因为我们可以通过行李进行0..5个可用步骤。在下一次迭代中,5被设置为NumberOfMoves0之间的随机值,因为现在我们可以通过包进行0..4步骤。在使用这些数字时,可用数字会减少,因此我们使用4来计算rand() % (RemainingNumberCount + 1)的下一个值。

NumberOfMoves计数器达到零时,NumberOfMoves应如下所示:

  1. 将当前值设置为与for..loop索引相同。
  2. 将行李中的所有数字设为for..loop
  3. false
  4. 分开

    <强>代码

    上述解决方案的代码如下:

    (将以下三个块一个接一个地放入主.cpp文件中)

    for..loop

    控制台类

    我创建此Console类,因为它可以轻松地重定向输出。

    下面的代码......

    #include "stdafx.h"
    #include <ctime> 
    #include <iostream>
    #include <string>
    
    class RandomBag {
    public:
        int Value = -1;
    
        RandomBag() {
            ResetBag();
    
        }
    
        void NextValue() {
            int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
    
            int NumberOfMoves = rand() % (RemainingNumberCount + 1);
    
            for (int i = 0; i < BagOfNumbersLength; i++)            
                if (BagOfNumbers[i] == 0) {
                    NumberOfMoves--;
    
                    if (NumberOfMoves == -1)
                    {
                        Value = i;
    
                        BagOfNumbers[i] = 1;
    
                        break;
    
                    }
    
                }
    
    
    
            if (RemainingNumberCount == 0) {
                RemainingNumberCount = 5;
    
                ResetBag();
    
            }
            else            
                RemainingNumberCount--; 
    
        }
    
        std::string ToString() {
            return std::to_string(Value);
    
        }
    
    private:
        bool BagOfNumbers[6]; 
    
        int RemainingNumberCount;
    
        int NumberOfMoves;
    
        void ResetBag() {
            RemainingNumberCount = 5;
    
            NumberOfMoves = rand() % 6;
    
            int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
    
            for (int i = 0; i < BagOfNumbersLength; i++)            
                BagOfNumbers[i] = 0;
    
        }
    
    };
    

    ...可以替换为......

    Console::WriteLine("The next value is " + randomBag.ToString());
    

    ...如果需要,可以删除此std::cout << "The next value is " + randomBag.ToString() << std::endl; 类。

    Console

    主要方法

    示例用法如下:

    class Console {
    public:
        static void WriteLine(std::string s) {
            std::cout << s << std::endl;
    
        }
    
    };
    

    示例输出

    当我运行程序时,我得到以下输出:

    int main() {
        srand((unsigned)time(0)); // Initialise random seed based on current time
    
        RandomBag randomBag;
    
        Console::WriteLine("First set of six...\n");
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        Console::WriteLine("\nSecond set of six...\n");
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        Console::WriteLine("\nThird set of six...\n");
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        Console::WriteLine("\nProcess complete.\n");
    
        system("pause");
    
    }
    

    结束语

    此程序是使用 Visual Studio 2017 编写的,我选择使用First set of six... The next value is 2 The next value is 3 The next value is 4 The next value is 5 The next value is 0 The next value is 1 Second set of six... The next value is 3 The next value is 4 The next value is 2 The next value is 0 The next value is 1 The next value is 5 Third set of six... The next value is 4 The next value is 5 The next value is 2 The next value is 0 The next value is 3 The next value is 1 Process complete. Press any key to continue . . . 将其设为Visual C++ Windows Console Application项目。

    我没有做任何特别特别的事情,所以代码也适用于早期版本的Visual Studio。

答案 10 :(得分:-1)

这是一个简单的随机发生器,约有。在0附近生成正负值的概率相等:

  int getNextRandom(const size_t lim) 
  {
        int nextRand = rand() % lim;
        int nextSign = rand() % lim;
        if (nextSign < lim / 2)
            return -nextRand;
        return nextRand;
  }


   int main()
   {
        srand(time(NULL));
        int r = getNextRandom(100);
        cout << r << endl;
        return 0;
   }

答案 11 :(得分:-1)

此代码产生从nm的随机数。

int random(int n, int m){
    return rand() % (m - n + 1) + n;
}

示例:

int main(){
    srand(time(0));

    for(int i = 0; i < 10; i++)
        cout << random(0, 99);
}