C++|自定義偽隨機數和標準庫中的隨機數

生成隨機數的能力在某些類型的程序中非常有用,特別是在遊戲、統計建模程序和需要對隨機事件建模的科學模擬中。以遊戲為例——如果沒有隨機事件,怪物總是以同樣的方式攻擊你,你總是會找到同樣的寶藏,地牢的佈局永遠不會改變,等等……這不會成為一個很好的遊戲。

1 自定義偽隨機數

基本思路是通過設置種子數、組合乘法、加法和迭代,再通過循環求餘便可以得出一些簡單的隨機數。

#include <iostream>

unsigned int PRNG()

{

....// our initial starting seed is 5323

....static unsigned int seed = 5323;

....// Take the current seed and generate a new value from it

....// Due to our use of large constants and overflow, it would be

....// hard for someone to casually predict what the next number is

....// going to be from the previous one.

....seed = 8253729 * seed + 2396403;

....// Take the seed and return a value between 0 and 32767

....return seed % 32768;

}

int main()

{

....// Print 20 random numbers

....for (int count=1; count <= 20; ++count)

....{

........std::cout << PRNG()*100/32768 << "\\t";

........// If we've printed 5 numbers, start a new row

........if (count % 5 == 0)

............std::cout << "\\n";

....}

....system("pause");

....return 0;

}

/*

23 4 67 22 77

44 30 45 99 0

59 85 87 74 56

42 42 65 20 17

*/

2 標準庫中的隨機數函數

標準庫中有兩個函數用於產生隨機數:

srand(int); //設置起始種子值

rand();// 產生隨機數

#include <iostream>

#include <stdlib.h> // for std::rand() and std::srand()/<stdlib.h>

int main()

{

....srand(5323); // set initial seed value to 5323

....// Print 20 random numbers

....for (int count=1; count <= 20; ++count)

....{

........std::cout << 100 + rand()/100 << "\\t";

........// If we've printed 5 numbers, start a new row

........if (count % 5 == 0)

............std::cout << "\\n";

....}

....system("pause");

....return 0;

}

/*

274 185 294 113 369

177 381 252 278 169

149 104 226 387 307

418 337 391 258 399

*/

3 選擇隨機性更強的隨機種子(時間)

頭文件<ctime>中的time_t time(time_t *seconds) 返回自紀元 Epoch(1970-01-01 00:00:00 UTC)起經過的時間,以秒為單位。如果 seconds 不為空,則返回值也存儲在變量 seconds 中。可以以這個秒數做為隨機數的種子值。/<ctime>

#include <iostream>

#include <cstdlib> // for std::rand() and std::srand()/<cstdlib>

#include <ctime> // for std::time()/<ctime>

int main()

{

....srand(static_cast(time(0)));

....// set initial seed value to system clock

....for (int count=1; count <= 20; ++count)

....{

........std::cout << rand()%100 << "\\t";

........// If we've printed 5 numbers, start a new row

........if (count % 5 == 0)

............std::cout << "\\n";

....}

....system("pause");

....return 0;

}

/*

70 22 48 60 15

87 41 20 56 0

51 7 68 37 4

98 62 96 80 18

*/

4 在兩個任意值之間生成隨機數

#include <iostream>

#include <time.h>

using namespace std;

// Generate a random number between min and max (inclusive)

// Assumes srand() has already been called

// Assumes max - min <= RAND_MAX

int getRandomNumber(int min, int max)

{

....double fraction = 1.0 / (RAND_MAX + 1.0);

....// evenly distribute the random number across our range

....return min + static_cast((max - min + 1) * (rand() * fraction));

}

int main()

{

....srand(time(0));

....for(int i=1; i<21; i++)

....{

........cout<<getrandomnumber>

........if(i%5==0)

............cout<<endl>

....}

....system("pause");

....return 0;

}

/*

184 172 153 106 197

111 156 167 200 169

124 105 167 185 113

171 144 104 176 192

*/

/<endl>

/<getrandomnumber>

為什麼我們在上面的函數中使用除法而不是模。簡而言之,模方法傾向於偏向於低數值。

讓我們考慮一下如果上面的函數是這樣的話會發生什麼:

min+(std::rand()%(max min+1));

看起來很相似,對吧讓我們來看看哪裡出了問題。為了簡化這個例子,假設rand()總是返回一個介於0和9(包括0和9)之間的隨機數,對於我們的示例,我們將選擇min=0,max=6因此,max-min+1是7。

現在讓我們計算所有可能的結果:

0 + (0 % 7) = 0

0 + (1 % 7) = 1

0 + (2 % 7) = 2

0 + (3 % 7) = 3

0 + (4 % 7) = 4

0 + (5 % 7) = 5

0 + (6 % 7) = 6

0 + (7 % 7) = 0

0 + (8 % 7) = 1

0 + (9 % 7) = 2

看看結果的分佈,結果0到2出現兩次,而3到6只出現一次。這種方法明顯偏向於較小的值,通過擴展,大多數涉及該算法的情況都會有類似的行為。

現在讓我們看看上面getRandomNumber()函數的結果,使用與上面相同的參數(rand()返回一個介於0和9(包括0和9)、min=0和max=6)之間的數字)在這種情況下,分數=1/(9+1)=0.1max-min+1仍然是7。

如果不用求模,而是用除法:

return min + (std::rand() % (max-min+1));

可能的結果:

0 + static_cast(7 * (0 * 0.1))) = 0 + static_cast(0) = 0

0 + static_cast(7 * (1 * 0.1))) = 0 + static_cast(0.7) = 0

0 + static_cast(7 * (2 * 0.1))) = 0 + static_cast(1.4) = 1

0 + static_cast(7 * (3 * 0.1))) = 0 + static_cast(2.1) = 2

0 + static_cast(7 * (4 * 0.1))) = 0 + static_cast(2.8) = 2

0 + static_cast(7 * (5 * 0.1))) = 0 + static_cast(3.5) = 3

0 + static_cast(7 * (6 * 0.1))) = 0 + static_cast(4.2) = 4

0 + static_cast(7 * (7 * 0.1))) = 0 + static_cast(4.9) = 4

0 + static_cast(7 * (8 * 0.1))) = 0 + static_cast(5.6) = 5

0 + static_cast(7 * (9 * 0.1))) = 0 + static_cast(6.3) = 6

這裡的偏向仍然是稍微朝著較低的數字(0、2和4出現兩次,而1、3、5和6出現一次),但它的分佈要均勻得多。

儘管getRandomNumber()比模方法更難理解,但我們還是提倡使用division方法,因為它產生的結果偏差較小。

5 使用隨機庫

一個可能更好的解決方案是使用第三方庫來為您處理所有這些內容。

-End-


分享到:


相關文章: