C++ generate_canonical均勻分佈亂數函數用法詳解

2020-07-16 10:04:33
標準均勻分布是一個在範圍 [0,1) 內的連續分布。generate_canonical() 函數模板會提供一個浮點值範圍在 [0,1) 內,且有給定的隨機位元數的標準均勻分布。它有 3 個模板引數:浮點型別、尾數的隨機位元的個數,以及使用的亂數生成器的型別。函數的引數是一個亂數生成器,因此最後一個模板引數可以推匯出來。下面是它可能的用法:
std::vector<double> data(8); // Container with 8 elements
std::random_device rd; // Non-determinstic seed source
std::default_random_engine rng {rd()}; // Create random number generator
std::generate(std::begin(data), std::end(data),[&rng] { return std::generate_canonical<double, 12> (rng); });
std::copy(std::begin(data),std::end(data),std::ostream_iterator<double> {std::cout, " "});
在 lambda 表示式中,被呼叫的 generate_canonical() 函數被用來作為 generate() 演算法的第三個引數。lambda 表示式會返回一個有 12 個隨機位元的 double 型別的隨機值,因此 generate() 會用這種資料來填充 data 中的元素。執行這些語句會產生如下輸出:

0.766197 0.298056 0.409951 0.955263 0.419199 0.737496 0.547764 0.91622

上面的輸出說明位數可能我們想要的要多,記住我們只指定了 12 個隨機位元。可以按如下方式限制輸出:
std::copy(std::begin(data), std::end(data),std::ostream_iterator<double>{std::cout << std::fixed<< std::setprecision (4) , " "});
流操作符被應用到每個輸出值,因此現在的輸出可能如下所示:

0.8514 0.5707 0.8322 0.6626 0.7026 0.8854 0.5427 0.8886

如果真的想得到這些位,可以用 hexfloat 以十六進位制的格式輸出這些值。

顯然,隨機位數越少,可能的隨機值的範圍越有限。可以通過將位數指定為這個型別的最大值來使範圍達到最大。下面是展示如何這麼做的程式碼:
std::vector<long double> data; // Empty container
std::random device rd; // Non-determinstic seed source
std::default_random_engine rng {rd()};//Create random number generator
std::generate_n(std::back_inserter(data), 10, [&rng]{return std::generate_canonical<long double,std::numeric_limits<long double>::digits>(rng); });
std::copy(std::begin(data), std::end(data),std::ostream_iterator<long double> {std:: cout, " "});
std::cout << std::endl;
注意,這和前面的程式碼有些區別。這次,generate_n() 的第一個引數是 data 容器的 back_insert_iterator,因此可以通過呼叫它的成員函數 push_back() 來新增元素。generate_canonical() 的第二個模板引數是 numeric_limits 物件的 long double 型別的成員變數的 digits 值。這是這個型別的位數的位元數,因此可以指定這個型別可能的隨機位元位的最大個數(在筆者系統上只有 53 個)。筆者得到了這樣的輸出,但你的可能是不同的:

0.426365 0.0635646 0.208444 0.198286 0.338378 0.490884 0.841733 0.975676 0.193322 0.346017