簡體   English   中英

如何在 C++ 中生成隨機數?

[英]How to generate a random number in C++?

我正在嘗試用骰子制作游戲,並且我需要在其中包含隨機數(以模擬骰子的側面。我知道如何使其介於 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"; 
}

不能很好地工作,因為當我運行程序幾次時,這是我得到的 output:

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

所以我想要一個每次都會生成不同隨機數的命令,而不是連續 5 次相同的隨機數。 有沒有命令可以做到這一點?

使用取模可能會在隨機數中引入偏差,具體取決於隨機數生成器。 有關更多信息,請參閱此問題。 當然,以隨機序列獲得重復數字是完全可能的。

嘗試一些 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;
}

有關 C++11 隨機數的更多信息,請參閱此問題/答案。 以上不是做到這一點的唯一方法,而是一種方法。

您的測試應用程序最根本的問題是您調用srand一次,然后調用rand一次並退出。

srand函數的重點是用隨機種子初始化偽隨機數序列

這意味着如果您在兩個不同的應用程序(具有相同的srand / rand實現)中將相同的值傳遞給srand ,那么您將在這兩個應用程序中獲得完全相同的rand()序列

但是,在您的示例應用程序中,偽隨機序列僅包含一個元素 - 從種子生成的偽隨機序列的第一個元素等於1 sec精度的當前時間。 那么你希望在輸出上看到什么?

顯然,當您碰巧在同一秒運行應用程序時 - 您使用相同的種子值 - 因此您的結果當然是相同的(正如 Martin York 在對該問題的評論中已經提到的那樣)。

實際上,您應該調用srand(seed)一次,然后多次調用rand()並分析該序列 - 它應該看起來是隨機的。

修正 1 - 示例代碼:

好的我明白了。 顯然口頭描述是不夠的(可能是語言障礙或什么...... :) )。

基於問題中使用的相同srand()/rand()/time()函數的老式 C 代碼示例:

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    {
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
        { /* bad value retrieved so get next one */ }

        printf( "%d,\t%d\n", n, n % 6 + 1 );
    }

    return 0;
}

^^^從程序的一次運行序列應該看起來是隨機的。

請注意,我不建議在生產中使用rand / srand函數,原因如下所述,我絕對不建議使用函數time作為隨機種子,因為 IMO 已經很明顯了。 這些對於教育目的來說很好,有時可以說明這一點,但對於任何認真的使用,它們大多是無用的。

修正 2 - 詳細說明:

重要的是要理解,到目前為止,還沒有C 或 C++ 標准特性(庫函數或類)明確地產生實際隨機數據(即由標准保證實際上是隨機的)。 解決這個問題的唯一標准特性是std::random_device ,不幸的是它仍然不提供實際隨機性的保證。

根據應用程序的性質,您應該首先決定是否真的需要真正隨機(不可預測)的數據。 當您確實需要真正的隨機性時,值得注意的情況是信息安全 - 例如生成對稱密鑰、非對稱私鑰、鹽值、安全令牌等。

然而,安全級隨機數是一個獨立的行業,值得單獨寫一篇文章。 我在我的這個答案中簡要討論了它們。

在大多數情況下,偽隨機數生成器就足夠了——例如用於科學模擬或游戲。 在某些情況下,甚至需要一致定義的偽隨機序列——例如,在游戲中,您可以選擇在運行時生成完全相同的地圖,以避免在您的分布中存儲大量數據。

最初的問題和重復出現的大量相同/相似的問題(甚至許多被誤導的“答案”)表明,首先區分隨機數和偽隨機數很重要,並了解什么是偽隨機數序列首先要意識到偽隨機數生成器的使用方式與使用真隨機數生成器的方式不同。

直觀地說,當您請求隨機數時 - 返回的結果不應該依賴於先前返回的值,也不應該依賴於之前是否有人請求過任何東西,也不應該依賴於在什么時刻、通過什么進程、在什么計算機上、從什么生成器和在它要求的是什么星系。 畢竟這就是“隨機”這個詞的意思——不可預測且獨立於任何事物——否則它不再是隨機的,對吧? 憑着這種直覺,在網絡上搜索一些魔法咒語以在任何可能的上下文中獲得這樣的隨機數是很自然的。

^^^這種直覺期望在所有涉及偽隨機數生成器的情況下都是非常錯誤和有害的——盡管對於真正的隨機數是合理的。

雖然存在有意義的“隨機數”概念(有點) - 沒有“偽隨機數”這樣的東西。 偽隨機數生成器實際上產生偽隨機數序列

偽隨機序列實際上總是確定性的(由其算法和初始參數預先確定)——也就是說,它實際上沒有任何隨機性。

當專家談論 PRNG 的質量時,他們實際上是在談論生成序列(及其顯着的子序列)的統計特性。 例如,如果您通過輪流使用它們來組合兩個高質量的 PRNG - 您可能會產生不良的結果序列 - 盡管它們分別生成了良好的序列(這兩個良好的序列可能只是相互關聯,因此組合不佳)。

特別是rand() / srand(s)對函數提供了一個單一的每進程非線程安全(!)偽隨機數序列,由實現定義的算法生成。 函數rand()產生[0, RAND_MAX]范圍內的值。

引自 C11 標准 (ISO/IEC 9899:2011):

srand函數使用參數作為新的偽隨機數序列的種子,隨后對rand調用將返回。 如果隨后使用相同的種子值調用srand ,則應重復偽隨機數序列。 如果在對srand進行任何調用之前調用rand ,則應生成與第一次調用srand時使用種子值為 1 時相同的序列。

許多人合理地期望rand()會產生一個范圍為0RAND_MAX的半獨立均勻分布數字序列。 好吧,它肯定應該(否則它沒用),但不幸的是,不僅標准不需要 - 甚至有明確的免責聲明指出“無法保證所產生的隨機序列的質量” 在某些歷史案例中, rand / srand實現質量確實很差。 即使在現代實現中,它很可能已經足夠好 - 但信任已被破壞並且不容易恢復。 除了它的非線程安全特性之外,它在多線程應用程序中的安全使用變得棘手且有限(仍然可能 - 您可以只從一個專用線程使用它們)。

新類模板std::mersenne_twister_engine<> (及其便利的std::mt19937 - std::mt19937 / std::mt19937_64具有良好的模板參數組合)提供了 C++11 標准中定義的每個對象的偽隨機數生成器。 使用相同的模板參數和相同的初始化參數,不同的對象將在使用 C++11 兼容標准庫構建的任何應用程序中的任何計算機上生成完全相同的每個對象輸出序列。 此類的優勢在於其可預測的高質量輸出序列和跨實現的完全一致性。

此外,C++11 標准中定義了更多 PRNG 引擎 - std::linear_congruential_engine<> (歷史上在某些 C 標准庫實現中用作公平質量的srand/rand算法)和std::subtract_with_carry_engine<> 它們還生成完全定義的依賴於參數的每個對象輸出序列。

現代 C++11 示例替換上面過時的 C 代碼:

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    {
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
        { /* bad value retrieved so get next one */ }

        std::cout << n << '\t' << n % 6 + 1 << '\n';
    }

    return 0;
}

先前使用std::uniform_int_distribution<> 的代碼版本

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution<unsigned> distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    {
        std::cout << distrib(gen) << ' ';
    }

    std::cout << '\n';
    return 0;
}

如果您使用的是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);
    }
};

每當您在 C++ 編程語言中對random number generation進行基本的網絡搜索時,這個問題通常是第一個彈出的問題! 我想把我的帽子扔進戒指,希望能更好地闡明 C++ 中偽隨機數生成的概念,以便將來不可避免地在網絡上搜索相同問題的編碼人員!

基礎知識

偽隨機數生成涉及利用確定性算法的過程,該算法生成性質近似於隨機數的數字序列。 我說近似相似,因為真正的隨機性在數學和計算機科學中是一個相當難以捉摸的謎 因此,為什么使用術語偽隨機來更迂腐正確!

在您實際使用 PRNG(即pseudo-random number generator ,您必須為算法提供一個初始值,通常也稱為種子 但是,使用算法本身之前,必須只設置一次種子!

/// 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頭文件中找到的所謂的線性同余生成器 此 PRNG 通過使用模算術的不連續分段函數起作用,即喜歡使用modulo operator '%'的快速算法。 以下是此 PRNG 的常見用法,關於@Predictability 提出的原始問題:

#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. 由於鴿巢原理std::rand()的常見用法消除了偽隨機數均勻分布的可能性。
  3. std::rand()通過std::srand( ( unsigned int )std::time( nullptr ) )播種的常見方式在技術上是不正確的,因為time_t被認為是受限制的類型 因此,不能保證time_tunsigned int的轉換

有關使用 C 的 PRNG 的整體問題以及如何可能規避這些問題的更多詳細信息,請參閱使用 rand() (C/C++):對 C 標准庫的 rand() 函數的建議

標准 C++ 方式

自從 ISO/IEC 14882:2011 標准(即 C++11)發布以來, random庫已經成為 C++ 編程語言的一部分。 這個庫配備了多個PRNG,以及不同的分布類型,例如: 均勻分布正態分布二項分布等。以下源代碼示例演示了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引擎,具有數值的均勻分布 (源代碼中引擎的名字聽起來很奇怪,因為它的名字來自它的時期2^19937-1 )。 該示例還使用std::random_device為引擎提供種子,該引擎從操作系統獲取其值(如果您使用的是 Linux 系統,則std::random_device/dev/urandom返回一個值)。

請注意,您不必使用std::random_device來播種任何 engine 您可以使用常量甚至chrono庫! 您也不必使用 32 位版本的std::mt19937引擎,還有其他選擇 有關random庫功能的更多信息,請參閱cplusplus.com

總而言之,C++ 程序員不應再使用std::rand() ,不是因為它不好,而是因為當前標准提供了更好的替代方案,更直接可靠 希望你們中的許多人發現這很有幫助,尤其是那些最近在網上搜索generating random numbers in c++

#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

可以從這里獲得用於生成隨機數的完整Randomer類代碼!

如果您在項目的不同部分需要隨機數,您可以創建一個單獨的類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";
}

您可以查看此鏈接作為示例,我如何使用此類Randomer類生成隨機字符串。 如果您願意,您也可以使用Randomer

每次生成不同的隨機數,連續六次不相同。

用例場景

我將可預測性的問題比作一袋六張紙,每張紙上都寫着 0 到 5 的值。 每次需要一個新值時,都會從袋子里抽出一張紙。 如果袋子是空的,則將數字放回袋子中。

...由此,我可以創建各種算法。

算法

一個包通常是一個Collection 我選擇了一個bool[] (也稱為布爾數組、位平面或位圖)來充當包的角色。

之所以選擇bool[]是因為每一項的索引已經是每張紙的價值了。 如果論文需要在上面寫任何其他內容,那么我會在它的位置使用Dictionary<string, bool> 布爾值用於跟蹤數字是否已被繪制。

被稱為RemainingNumberCount的計數器被初始化為5 ,以便選擇隨機數。 這使我們不必在每次想畫一個新數字時計算還剩多少張紙。

為了選擇下一個隨機值,我使用for..loop來掃描索引包,並使用一個計數器在indexfalse時進行計數,稱為NumberOfMoves

NumberOfMoves用於選擇下一個可用號碼。 NumberOfMoves首先設置為05之間的隨機值,因為我們可以通過包進行 0..5 個可用步驟。 在下一次迭代中,將NumberOfMoves設置為04之間的隨機值,因為現在我們可以通過袋子進行 0..4 步。 隨着數字的使用,可用數字會減少,因此我們改為使用rand() % (RemainingNumberCount + 1)來計算NumberOfMoves的下一個值。

NumberOfMoves計數器達到零時, for..loop應如下所示:

  1. 將當前值設置為與for..loop的索引相同。
  2. 將包中的所有數字設置為false
  3. 脫離for..loop

代碼

上述解決方案的代碼如下:

(將以下三個塊依次放入主.cpp文件中)

#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 類是因為它可以很容易地重定向輸出。

在代碼下面...

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");

}

示例輸出

當我運行程序時,我得到以下輸出:

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 Studio 2017編寫的,我選擇使用.Net 4.6.1使其成為Visual C++ Windows Console Application項目。

我在這里沒有做任何特別的事情,所以代碼也應該適用於早期版本的 Visual Studio。

此代碼生成從nm隨機數。

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

例子:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

這是一個解決方案。 創建一個返回隨機數的函數,並將其放在 main 函數之外以使其成為全局變量。 希望這可以幫助

#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;
}

我知道如何在不使用任何頭文件、編譯器內部函數或其他任何東西的情況下在 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。

隨機每個 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);
}

這是我的 5 美分:

// System includes
#include <iostream>
#include <algorithm>
#include <chrono>
#include <random>

// Application includes

// Namespace
using namespace std;

// Constants
#define A_UNUSED(inVariable) (void)inVariable;


int main(int inCounter, char* inArguments[]) {

    A_UNUSED(inCounter);
    A_UNUSED(inArguments);

    std::random_device oRandomDevice;
    mt19937_64 oNumber;
    std::mt19937_64::result_type oSeed;
    std::mt19937_64::result_type oValue1;
    std::mt19937_64::result_type oValue2;

    for (int i = 0; i < 20; i++) {

        oValue1 = (std::mt19937_64::result_type) std::chrono::duration_cast<std::chrono::seconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
        oValue2 = (std::mt19937_64::result_type) std::chrono::duration_cast<std::chrono::microseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
        oSeed = oRandomDevice() ^ (oValue1 + oValue2);
        oNumber.seed(oSeed);

        cout << "oNumber: " << oNumber << "\n";
        cout << "oNumber.default_seed: " << oNumber.default_seed << "\n";
        cout << "oNumber.initialization_multiplier: " << oNumber.initialization_multiplier << "\n";
        cout << "oNumber.mask_bits: " << oNumber.mask_bits << "\n";
        cout << "oNumber.max(): " << oNumber.max() << "\n";
        cout << "oNumber.min(): " << oNumber.min() << "\n";
        cout << "oNumber.shift_size: " << oNumber.shift_size << "\n";
        cout << "oNumber.state_size: " << oNumber.state_size << "\n";
        cout << "oNumber.tempering_b: " << oNumber.tempering_b << "\n";
        cout << "oNumber.tempering_c: " << oNumber.tempering_c << "\n";
        cout << "oNumber.tempering_d: " << oNumber.tempering_d << "\n";
        cout << "oNumber.tempering_l: " << oNumber.tempering_l << "\n";
        cout << "oNumber.tempering_s: " << oNumber.tempering_s << "\n";
        cout << "oNumber.tempering_t: " << oNumber.tempering_t << "\n";
        cout << "oNumber.tempering_u: " << oNumber.tempering_u << "\n";
        cout << "oNumber.word_size: " << oNumber.word_size << "\n";
        cout << "oNumber.xor_mask: " << oNumber.xor_mask << "\n";
        cout << "oNumber._Max: " << oNumber._Max << "\n";
        cout << "oNumber._Min: " << oNumber._Min << "\n";
    }

    cout << "Random v2" << endl;
    return 0;
}

一個非常有主見的答案

c++ <random>庫違反了軟件工程的最佳原則之一:“簡單的事情完成簡單、復雜、不常見的事情可能會更復雜一些。”

相反,他們甚至使簡單和常見的用例也變得過於復雜,只是因為他們患有文化疾病,害怕“這還不夠普遍”之類的評論。

結果,現在每當您想要一個簡單的隨機數時,您都必須查看文檔,閱讀帶有文本牆的堆棧溢出,美化這個糟糕的設計,而不僅僅是一個易於記憶的一兩個襯里。 (Common Lisp 更實用:( (random 5)從 0..4 產生均勻分布的整數,而(random 1.0)產生 0.0..1.0 之間的實數。這是最常見的用例,觸手可及。如果你需要更復雜的東西,你必須找到包和庫或者自己做。)

只需計算全球每個人在了解 header 及其內容上浪費時間的累計工時,看看它有多糟糕。

即使我現在在浪費時間,寫這個答案,你也浪費時間,閱讀它,只是因為他們創造了一個復雜的謎題,這與其他現代可憎的東西有着相似的精神,比如 Vulkan API。

那么,該如何應對呢? 浪費一次時間,為最常見的用例編寫一個 header 文件,然后在需要時重新使用它。

這是一個簡單的隨機生成器,大約有。 在 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;
   }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM