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