C++ 隨機數初探

2021-02-20 CPP開發者

(給CPP開發者加星標,提升C/C++技能)

https://blog.csdn.net/qq_34784753/article/details/79600809

【導讀】:在 C++ 11之前,C 和 C++ 都依賴一個簡單的 C 庫函數 rand 來生成隨機數,rand只能生成均勻分布的隨機數,那麼當我們需要非均勻分布時,應該怎麼辦?C++11種引入了新的隨機數接口,我們一起來看看吧。

--- 以下是正文 ---

C++ 中的隨機數

在 C++ 程序中,在新標準出現之前,C 和 C++ 都依賴一個簡單的 C 庫函數 rand 來生成隨機數,但是,這個函數生成的是均勻分布的偽隨機數,每個隨機數的範圍在 0 和一個系統相關的最大值(至少為 32767)之間。

rand 函數有一些問題:即使不是大多數,也有很多程序需要不通範圍的隨機數。一些應用需要隨機浮點數。一些程序需要非均勻分布的隨機數。而在編寫程序為了解決這些通常會轉換 rand 生成的隨機數的範圍、類型或者是分布時,常常會引入非隨機性。

在 C++ 11 標準中,定義在頭文件 random 中的隨機數庫通過一組協作的類來解決這些問題,主要用到的是兩個類:

其中,一個引擎類可以生成 unsigned 隨機數列,一個分布使用一個引擎類生成指定類型的,在給定範圍內的,服從指定概率分布的隨機數。

1. 隨機數引擎和分布

隨機數引擎是函數對象類,他們定義了一個調用運算符,該運算符不接受參數並返回一個隨機的 unsigned 整數。我們可以通過調用一個隨機數引擎對象來生成原始隨機數。

default_random_engine e;    for(size_t i=0; i<10; i++)        cout << e() <<endl;

在上面這幾行的代碼中,定義了一個名為 e 的 default_random_engine 的對象。在 for 循環內,我們調用對象 e 來獲得下一個隨機數。

1.1 分布類型和引擎

為了得到一個在指定範圍內的數,我們一用一個分布類型的對象:

uniform_int_distribution<unsigned> u(0,9);default_random_engine e;    for (size_t i =0;i<10; i++)            cout<<u(e)<<" ";cout<< endl;

上面的代碼輸入如下:

上面的程序中,我們將 u 定義為 uniform_int_distribution<unsigned> 。這種類型生成均勻分布的 unsigned 值。當我們定義一個這種類型的對象時,可以提供想要的最小值和最大值。在上面這段代碼中,u(0,9) 表示我們希望得到 0 到 9 之間(包含)的數。隨機數分布類會使用包含的範圍,從而我們可以得到給定整形的每個可能值。

類似引擎類型,分布類型也是函數對象類。分布類型定義了一個調用運算符,它接受一個隨機數引擎作為參數。分布對象使用它的引擎參數生成隨機數,並將其映射到指定的分布。

傳遞給分布對象的是引擎對象本身,也就是 u(e),如果我們將調用寫為 u(e()),含義就變為將 e 生成的下一個值傳遞給 u,這會導致一個編譯錯誤。我們傳遞的是引擎本身,而不是他生成的下一個值,原因是某些分布可能需要調用引擎多次才能得到一個值。

1.2 使用引擎生成一個數值序列

隨機數發生器有一個特性,也就是即使生成的樹看起來是隨機的,但是對於一個給定的發生器,每次運行程序它都會返回相同的數值序列。序列不變這一事實在 調試 的時候十分有用,但是另一方面,使用隨機數發生器的程序也必須考慮到這一特性。

下面介紹一個例子,需要一個函數生成一個 vector,包含 100 個均勻分布在 0 到 9 之間的隨機數。一種錯誤的方法是使用下面的代碼:

vector<unsigned >bad_randVec(){    default_random_engine e;    uniform_int_distribution<unsigned >u(0,9);    vector<unsigned >ret;    for(size_t i = 0;i<100;i++)        ret.push_back(u(e));    return ret;}vector<unsigned >v1(bad_randVec());vector<unsigned >v2(bad_randVec());cout << ((v1==v2) ? "equal" : "not equal") << endl;

上面這段代碼會輸出 equal,因為 vector v1 和 v2 具有相同的值。

正確的定義方法是 將引擎和關聯的分布對象定義為 static 的:

vector<unsigned >good_randVec(){            static default_random_engine e;    static uniform_int_distribution<unsigned > u(0,9);    vector<unsigned > ret;    for(size_t i = 0; i<100;i++)        ret.push_back(u(e));    return ret;}

由於 e 和 u 都是 static 的,因此它們在函數調用之間會保持住狀態。第一次調用會使用 u(e) 生成的序列中的前 100 個隨機數,第二次調用會獲得接下來 100 個。以此類推。

注意,一個給定的隨機數發生器已知會生成相同的隨機數序列。一個函數如果定義了局部的隨機數發生器,應該將其(包括引擎和分布對象)定義為 static 的。否則,每次調用函數都會生成相同的序列。

1.3 設置隨機數發生器種子

隨機數發生器會生成相同的隨機數序列這一特性在調試中很有用。但是,一旦我們的程序調試完畢,我們通常希望每次運行程序都會生成不同的隨機結果,可以通過提供一個種子(seed)來達到這個目的。種子就是一個數值,殷勤可以利用它從序列中一個新位置重新開始生成隨機數。

為引擎設置種子有兩種方式:

(1)在創建引擎對象時提供種子

(2)調用引擎的 seed 成員

default_random_engine e1;       default_random_engine e2(2147483646);       default_random_engine e3;e3.seed(32767);             default_random_engine e4(32767);    for(size_t i = 0;i != 10; i++){    if (e1() == e2())        cout<<"unseeded match at iteeration: "<<i<<endl;    if (e3() != e4())        cout<<"seeded differs at itertion: "<<i<<endl;}

設置種子最常用的方法是調用系統函數 time ,這個函數定義再頭文件 ctime 中,它返回一個特定時刻到當前經過了多少秒。函數 time 接受單個指針參數,它指向用於寫入時間的數據結構。如果此指針為空,則函數簡單的返回時間:

default_random_engine e1(time(0));  

但是,由於 time 返回以秒計的時間,因此這種方式只適用於生成種子的間隔為秒級或更長的應用。

2. 其他隨機數分布2.1 生成隨機實數

程序常常需要一個隨機浮點數源。特別是程序經常需要 0 到 1 之間的隨機數。

可一定以一個 uniform_real_distribution 類型的對象,並讓標準庫來處理從隨機整數到隨機浮點數的映射。與處理 uniform_int_distribution 一樣,在定義對象時,我們指定最小值和最大值。

default_random_engine e;        uniform_real_distribution<double >u(0,1);for(size_t i =0;i<10;i++)    cout<<u(e)<<" "; cout<<endl;

此外,當我們對分布函數不指定默認生成的類型參數時,程序會自動賦予一個類型,生成浮點值得分布類型默認生成 double 類型,生成整型值的分布類型默認生成 int 類型,如下:

uniform_real_distribution<>u(-1,1); 

2.2 生成非均勻分布的隨機數

除了生成上面的均勻分布,C++ 11 還規定了可以生成 20 種不同的分布類型,比如 均勻分布uniform,正態分布normal,二項分布binomial,泊松分布poisson,學生分布 student 等等,相關函數可以查看相應的函數(具體可以參考 C++ Primer 781頁)。

- EOF -

關於 C++ 隨機數,歡迎在評論中和我探討。覺得文章不錯,請點讚和在看支持我繼續分享好文。謝謝!

關注『CPP開發者』

看精選C++技術文章 . 加C++開發者專屬圈子

↓↓↓

點讚和在看就是最大的支持❤️

相關焦點

  • C++隨機排序容器中的元素
    shuffle方法,c++也不例外。不過c++將shuffle放在了<algorithm>中而不是像其他語言一樣在random裡,同時c++17刪除了原先的random_shuffle新的程序應該使用c++11添加進去的std::shuffle。其中一個好處是新的函數在可以自定義隨機數生成方法的同時保證了更好的安全性。
  • C語言/C++中如何產生隨機數
    需要說明的是,iostream頭文件中就有srand函數的定義,不需要再額外引入stdlib.h;而使用time()函數需要引入ctime頭文件。使用rand()函數獲取一個隨機數如果你只要產生隨機數而不需要設定範圍的話,你只要用rand()就可以了:rand()會返回一隨機數值, 範圍在0至RAND_MAX 間。
  • 隨機數本質,C語言的隨機數與隨機種子
    引言在實際編程中,我們經常會用到隨機數這個概念,其實也是一個偽隨機數,實際上並不是一個真正的隨機數,但是也足夠我們使用了。在C語言中,編寫一些關於遊戲之類的程序時就需要用到隨機數了。同時C語言也提供了一個標準庫裡面一個函數來產生隨機數,而對於隨機數的產生是根據種子(根據一個數值按照某種公式計算的)來變化的,種子 與隨機數之間符合正態分布(高斯分布)。
  • C++入門基礎系列:走進信息學奧賽,如何產生隨機數?
    進入本文的主題:C++中,如何產生隨機數?02如何產生隨機數?分三步驟,第1、2步為固定語法格式;第3步根據題目具體要求編寫表達式。需要引入兩個頭文件:cstdlib、ctime;在程序開頭處編寫固定格式(產生隨機種子):srand(time(NULL));這樣能夠保證程序每次運行時,隨機數都不同。在需要產生隨機數的地方調用rand()函數,構造表達式即可。
  • 什麼帳戶隨機數?隨機數的作用是什麼?
    (什麼帳戶隨機數?隨機數的作用是什麼?| 金色百科)  那麼隨機數在比特幣區塊鏈中的作用是什麼呢?  在比特幣區塊鏈中,所有參與節點都在遍歷尋找一個隨機數,節點算力輸出越大就越有可能遍歷到這個隨機數,也就能夠搶到這一輪的記帳權,這個過程通常被稱為「挖礦」。各參與節點在遍歷隨機數的過程中也在不斷地驗證其他節點的交易信息,搶到記帳權的節點會把所有相關的信息廣播出來,全網中所有參與節點在共同驗證了廣播出來的信息之後就會形成一個區塊,緊接在上一個區塊的後面,形成一條區塊數據鏈。
  • C++ 優先隊列priority_queue
    Container 還必須要支持隨機訪問,並且有 front()、push_back()、pop_back() 等函數這樣來看只有 std::vector、std::deque 滿足容器條件了,而優先隊列中使用的默認參數也是 std::vector。
  • 隨機數生成
    如利用蒙特卡羅法估計測量數據的不確定時,就需要使用隨機數生成器來傳遞分布。本書中大量的例子都用到了示例數據,創建這些數據就利用了隨機數生成器。Igor具有強大的統計分析功能,提供了大量函數和命令,用於分布計算、參數估計、假設檢驗、回歸分析等。其中僅偽隨機數生成器就有12種,可以生成滿足常見分布要求的隨機數,如二項分布隨機數、均勻分布隨機數、高斯分布隨機數、洛倫茲分布隨機數等。
  • excel隨機數函數是什麼?excel怎樣生成隨機數?
    本篇將介紹excel隨機數函數是什麼?excel怎樣生成隨機數?有興趣的朋友可以了解一下!一、前言excel是我們工作中很常用的一款表格製作工具,它不僅僅只是用來製作表格,它還能對表格中的數據進行處理(比如:運算、排序、篩選等)。
  • 密碼學的骰子——隨機數
    根據一般定義,隨機數應該具有以下三個性質:隨機性,不存在統計學偏差,是完全雜亂的數列,即分布均勻性和獨立性;不可預測性,不能從過去的隨機數數列推測出下一個出現的數;不可重現性,不能重現相同的數列。我們在平時編程開發裡用到的隨機數,一般都只滿足第一個條件,這種只滿足隨機性分布的隨機數,就叫做偽隨機數或弱偽隨機數。這是因為程式語言提供的隨機數生成方法(學名叫偽隨機數生成器)是靠軟體算法實現的,既然是算法,那就必定遵循了一定的規律,也就有被預測的可能。
  • C++設計模式Singleton初探
    C++設計模式Singleton初探Singleton模式的特點是保證一個類僅能創建一個實例,即程序運行中
  • Excel生成隨機數的技巧,隨機數發生器,你用過嗎
    Excel獲取隨機數從概率分布中產生隨機樣本,其基本原理是隨機數的概念。隨機數是均勻分布在0和1之間的數。從技術上講,電腦不能產生真正的隨機數,因為它們必須使用一個可預測的算法。但是,設計用於產生一個數列的算法,似乎是隨機的。在 Excel中,我們可以使用函數RAND()在任何一個單元格之中產生隨機數。
  • python中的隨機數
    random模塊是python中的隨機數模塊,也是比較常用的模塊之一。
  • 隨機數大家都會用,但是你知道生成隨機數的算法嗎?
    再不濟我們每周的抽獎都是用隨機數抽出來的,我們用隨機數的時候,往往都會加一個前綴,說它是偽隨機數,那麼這個偽隨機數的偽字該怎麼解釋,什麼又是真隨機數呢?計算機算法得出的各種隨機數之所以是偽隨機數是因為它們的結果都是可以預測的,只要我們知道算法和起始狀態以及各種參數,就可以預測下一次隨機出來的結果。而真隨機數則無法預測,就是純粹隨機的。但問題來了,拋硬幣和擲骰子這些物理現象又是真的隨機嗎?如果我們知道了硬幣的起始狀態以及拋擲的角度和力度,是不是可以預測硬幣拋擲的結果呢?
  • C++機器學習庫介紹
    +11 -lboost_serialization -lshark -lcblas用Shark實現線性回歸初始化階段我們將從包含線性回歸的庫和頭函數開始:#include <bits/stdc++.h> //所有c++標準庫的頭文件#include <shark/
  • c++11新特性,所有知識點都在這了!
    c++11新特性吧,你是怎麼回答的呢?,然而這種問題其實都可以通過c++11引入的智能指針來解決,相反我還認為這種內存管理還是c++語言的優勢,因為盡在掌握。內存對齊什麼是內存對齊理論上計算機對於任何變量的訪問都可以從任意位置開始,然而實際上系統會對這些變量的存放地址有限制,通常將變量首地址設為某個數N的倍數,這就是內存對齊。
  • Excel隨機數生成方法,包括準確生成不重複和小數隨機數
    在 Excel 中,生成隨機數有兩個函數,分別為Rand函數和RandBetween函數,前者用於生成 0 到 1 之間的隨機數,後者用於生成指範圍的隨機數。它們生成的隨機數中都會產生重複值,如果要生成不重複的隨機數得用變通的方法,通常有兩種方法,一種為先生成種子再生成不重複的隨機數,另一種為用多個函數生成。
  • 玩轉Python 中的隨機數
    開發中我們經常遇到需要隨機數的場景,比如為了用戶密碼更安全我們有時會加鹽,也就是將用戶原密碼連接上一串隨機字符然後加密保存,又比如我們可能需要隨機展示某張圖片等等。今天,我們就來理一理 Python 中的隨機數的玩法,當然,這裡只涉及標準庫。
  • numpy產生隨機數匯總
    random()產生的隨機數的分布非均勻分布,numpy.random.rand()產生的隨機數的分布為均勻分布。np.random.random((3,3))  ##產生一個[0,1)之間的形狀為(3, 3)的數組。
  • 單片機隨機數:rand(),srand()
    通常的做法是以這樣一句代碼srand((unsigned) time(NULL));來取代,這樣將使得種子為一個不固定的數, 這樣產生的隨機數就不會每次執行都一樣了。有討論如下:1.C的函數庫之所以沒有把使用系統時鐘初始化隨機種子這步重要的操作直接放進rand函數的實現中,我覺得至少有三個原因:(1)可以高效產生連續的隨機數,不用每次都初始化;(2)給程式設計師以更高的靈活性,因為可能在要求較高的場合,應該使用更好的的數據做種子,而不是系統時鐘;(3)對於只是想產生大量偽隨機數來盡興某種驗證或者統計
  • 區塊鏈中隨機數的實現
    當我們談論計算機系統中的隨機性時,我們真正指的是偽隨機性,即儘可能模擬出現實世界應有的隨機性,使之近乎於「真正的隨機性」。以密碼學安全偽隨機數生成器為例,這是一個非常強大的隨機性模擬。隨機數在隱私技術和密碼學中發揮著重要作用。令人驚豔的是,通過生成一個隨機數來對一條消息進行運算(XOR),提供了一種簡單但十分強大的加密方案。