c++11僞隨機數生成庫:random

隨機數引擎

linear_congruential_engine 實現線性同餘算法
mersenne_twister_engine 實現梅森纏繞器算法
subtract_with_carry_engine 實現帶進位減(一種延遲斐波那契)算法
以上三個引擎都是類模板,它們第一個模板參數都是UIntType,表示生成數字的類型,且在內部被定義爲result_type成員類型,其他模板參數是特定算法需要的參數。
它們的構造函數類似,以linear_congruential_engine爲例說明:

linear_congruential_engine() : linear_congruential_engine(default_seed) {} (1)
explicit linear_congruential_engine(result_type value); (2)
template <class Sseq>
explicit linear_congruential_engine(Sseq& s); (3)	
linear_congruential_engine(const linear_congruential_engine&); (4)(隱式聲明)
構造引擎並初始化狀態。
(1)默認構造函數。以 default_seed 播種引擎。
僅若 Sseq 符合種子序列要求,(3)才參與重載決議。尤其是若 Sseq 可轉換爲 result_type,則從候選函數集中排除該重載。

類 std::seed_seq 滿足種子序列的要求。一個 seed_seq 對象消耗整數值數列,並基於消耗的數據生成請求數量的無符號整數值 i,0<=i < 2^32。產生的值分佈在整個 32 位範圍上,即使消耗的值接近。seed_seq 的構造函數和 generate 函數聲明如下:

seed_seq();
seed_seq(const seed_seq&) = delete;
template <class InputIt>
seed_seq(InputIt begin, InputIt end);
template <class T>
seed_seq(std::initializer_list<T> il);
template <class RandomIt>
void generate(RandomIt begin, RandomIt end);

一個簡單的例子:

#include <random>
#include <cstdint>
#include <iostream>
int main()
{
    std::seed_seq seq{1,2,3,4,5};
    std::vector<std::uint32_t> seeds(10);
    seq.generate(seeds.begin(), seeds.end());
    for (std::uint32_t n : seeds) {
        std::cout << n << '\n';
    }
    return 0;
}

回到三個隨機數引擎上,它們還有一些公共的成員函數:

void seed(result_type value = default_seed);
template <class Sseq>
void seed(Sseq& seq);
用新種子值重新初始化隨機數引擎的內部狀態。

result_type operator()();
生成僞隨機數。令引擎狀態前進一個位置。

void discard(unsigned long long z);
令內部狀態前進 z 次。等價於調用 operator() z 次並捨棄結果。

static constexpr result_type min();
返回隨機數引擎潛在生成的最小值。此值等於 0ustatic constexpr result_type max();
返回隨機數引擎潛在生成的最大值。

此外還有以下非成員函數:

operator==
operator!=
比較兩個僞隨機數引擎的內部狀態

operator<<
operator>>
執行僞隨機數引擎的流輸入和輸出
前者序列化引擎的內部狀態爲一或多個空格分隔的十進制數序列,並插入到流。後者能將十進制數序列還原爲原引擎。

隨機數引擎適配器

template <class Engine, size_t P, size_t R>
class discard_block_engine;
discard_block_engine 忽略基礎引擎所產生的一定量數據。
生成器只從基礎引擎生成的每個 P 大小的塊保留 R 個數,捨棄剩餘的數。
P - 塊大小。必須大於 0 。
R - 每塊用的數字數。必須大於 0 且不大於 P 。

template <class Engine, std::size_t W, class UIntType>
class independent_bits_engine;
class independent_bits_engine 將一個隨機數引擎的輸出適配爲具有特定比特數的數字。
W - 生成數字應有的比特數,其他位填0。必須大於零,且不大於 std::numeric_limits<UIntType>::digits。
UIntType - 生成的隨機數類型。類型必須是無符號整數類型。
	std::independent_bits_engine<default_random_engine, 4, unsigned> e;
	std::cout << '[' << e.min() << ", " << e.max() << ']' << std::endl; //輸出結果是[0, 15]

template <class Engine, std::size_t K>
class shuffle_order_engine;
shuffle_order_engine 打亂基礎引擎生成的隨機數。
它維護一個大小爲 K 的表,並在請求時隨機地從該表派送被選擇數,將它替換爲基礎引擎生成的數。

非確定隨機數

std::random_device 是生成非確定隨機數的均勻分佈整數隨機數生成器。
std::random_device 可以以實現定義的僞隨機數引擎實現,在某些實現中可能每個 std::random_device 對象生成同一數值序列。
std::random_device 一般作爲其他隨機數引擎的種子使用。

random_device() : random_device(/*implementation-defined*/) {}
explicit random_device(const std::string& token);
標準期待 token 指定產生隨機數字的字節設備,比如 linux 下的 /dev/random 。
random_device(const random_device& ) = delete;
random_device& operator=(const random_device&) = delete;
result_type operator()();
生成均勻分佈的非確定隨機值。
static constexpr result_type min();
返回隨機數引擎潛在生成的最小值。此值等於 0ustatic constexpr result_type max();
返回隨機數引擎潛在生成的最大值。此值等於 std::numeric_limits<unsigned int>::max()

簡單例子:

#include <iostream>
#include <random>

int main()
{

    std::uniform_int_distribution<int> d(0, 10);

    std::random_device rd1; // 使用 RDRND 或 /dev/urandom
    for(int n = 0; n < 10; ++n)
        std::cout << d(rd1) << ' ';
    std::cout << '\n';

    std::random_device rd2("/dev/random"); // Linux 上更慢
    for(int n = 0; n < 10; ++n)
        std::cout << d(rd2) << ' ';
    std::cout << '\n';
}

預定義生成器

minstd_rand0
std::linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>
由 Lewis、Goodman 及 Miller 發現於 1969,由 Park 與 Miller 於 1988 採納爲“最小標準”

minstd_rand
std::linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>
較新的“最小標準”,爲 Park、 Miller 及 Stockmeyer 於 1993 推薦

mt19937
std::mersenne_twister_engine<std::uint_fast32_t, ...
32 位梅森纏繞器,由松本與西村發現於 1998

mt19937_64
std::mersenne_twister_engine<std::uint_fast64_t, ...
64 位梅森纏繞器,由松本與西村設計於 2000

ranlux24_base	std::subtract_with_carry_engine<std::uint_fast32_t, 24, 10, 24>
ranlux48_base	std::subtract_with_carry_engine<std::uint_fast64_t, 48, 5, 12>

ranlux24
std::discard_block_engine<std::ranlux24_base, 223, 23>
24 位 RANLUX 生成器,由 Martin Lüscher 與 Fred James 設計於 1994

ranlux48
std::discard_block_engine<std::ranlux48_base, 389, 11>
48 位 RANLUX 生成器,由 Martin Lüscher 與 Fred James 設計於 1994

knuth_b
std::shuffle_order_engine<std::minstd_rand0, 256>

default_random_engine
實現定義

一般來說像下面這樣用:

#include <random>
#include <iostream>
int main()
{
    std::random_device rd;
    std::default_random_engine gen(rd());
    for(int i=0; i<10; ++i)
        std::cout << gen() << ' ';
    return 0;
}

用於產生滿足特定分佈數字的類/類模板/函數模板

generate_canonical

template <class RealType, size_t bits, class Generator>
RealType generate_canonical(Generator& g);
生成範圍 [0, 1) 中的隨機浮點值。
RealType - 浮點類型。
bits - 精度,如果該值大於RealType的精度,將使用 numeric_limits<RealType>::digits 。

簡單例子:

#include <random>
#include <iostream>
 
int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    for(int n = 0; n < 10; ++n) {
        std::cout << std::generate_canonical<double, 10>(gen) << ' ';
    }
}

均勻分佈

uniform_int_distribution
產生在一個範圍上均勻分佈的整數值。(類模板)
uniform_real_distribution
產生在一個範圍上均勻分佈的實數值。(類模板)

伯努利分佈

bernoulli_distribution
產生伯努利分佈上的 bool 值。(類)
binomial_distribution
產生二項分佈上的整數值。(類模板)
negative_binomial_distribution
產生負二項分佈上的整數值。(類模板)
geometric_distribution
產生幾何分佈上的整數值。(類模板)

泊松分佈

poisson_distribution
產生泊松分佈上的整數值。(類模板)
exponential_distribution
產生指數分佈上的實數值。(類模板)
gamma_distribution
產生 Γ 分佈上的實數值(類模板)
weibull_distribution
產生威布爾分佈上的實數值。(類模板)
extreme_value_distribution
產生極值分佈上的實數值。(類模板)

正態分佈

normal_distribution
產生標準正態(高斯)分佈上的實數值。(類模板)
lognormal_distribution
產生對數正態分佈上的實數值。(類模板)
chi_squared_distribution
產生 χ2 分佈上上的實數值。(類模板)
cauchy_distribution
產生柯西分佈上的實數值。(類模板)
fisher_f_distribution
產生費舍爾 F 分佈上的實數值。(類模板)
student_t_distribution
產生學生 t 分佈上的實數值。(類模板)

採樣分佈

discrete_distribution
產生離散分佈上的隨機整數。(類模板)
piecewise_constant_distribution
產生分佈在常子區間上的實數值。(類模板)
piecewise_linear_distribution
產生分佈在定義的子區間上的實數值。(類模板)

以上的類或類模板使用方式都是類似的,構造函數根據參數生成概率分佈函數,使用operator()()根據關聯的概率分佈函數生成隨機數。
他們主要有以下常用成員:

成員類型result_type,operator()()的返回值。

void reset();
重置分佈對象的內部狀態

template <class Generator>
result_type operator()(Generator& g);
根據關聯的概率分佈函數生成隨機數。由調用 g.operator() 獲得熵。

result_type min() const;
返回分佈潛在生成的最小值。

result_type max() const;
返回分佈潛在生成的最大值。

看一個正態分佈的例子:

#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>
#include <cmath>
int main()
{
    std::random_device rd{};
    std::mt19937 gen{rd()};

    // 值最可能接近平均
    // 標準差影響生成的值距離平均數的分散
    std::normal_distribution<> d{5,2};

    std::map<int, int> hist;
    for(int n=0; n<50000; ++n)
    {
        ++hist[std::round(d(gen))];
    }
    for(auto &p : hist)
    {
        std::cout << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
    return 0;
}

輸出是:

-4
-3
-2
-1
 0 **
 1 *******
 2 ***************
 3 ******************************
 4 *******************************************
 5 *************************************************
 6 *******************************************
 7 ******************************
 8 ****************
 9 ******
10 **
11
12
13
14

參考 cppreference.com cplusplus.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章