關於C++,Java和Python中的隨機數生成法
首先我們來說說C++中的隨機數生成:
我們知道在C++用函式rand()獲取的是一個0 ~ RAND_MAX之間的一個隨機數。其中RAND_MAX的值為32767。
首先我們來分析兩個程式:
#include <iostream> #include <string.h> #include <stdlib.h> #include <time.h> #include <stdio.h> using namespace std; int main() { srand(time(0)); for(int i=0;i<10;i++) { int t = rand(); cout<<t<<endl; } return 0; }
#include <iostream> #include <string.h> #include <stdlib.h> #include <time.h> #include <stdio.h> using namespace std; int main() { for(int i=0;i<10;i++) { srand(time(0)); int t = rand(); cout<<t<<endl; } return 0; }
我們分別輸出這兩個程式的結果會發現,第一個程式結果為:
而第二個程式的結果是:
很明顯,我們希望得到第一種結果。那麼把srand(time(0))放到迴圈體內和放到迴圈體外有什麼不同?
現在我來詳細解釋上面的結果。
我們首先要明確,對於函式srand(int num)和函式rand()它們是定義在標頭檔案<stdlib.h>裡的。
而srand(int num)函式的作用是散佈num個隨機數,當用rand()時,獲取的是散佈的這num個數中的任意一個。
rand()函式返回隨機數序列中的下一個數,實際上srand(int num)函式是一個偽隨機數序列,序列中的每一個數是由對
time(0)返回的是一個整數,這個整數的值等於1970年1月1日0時0分0秒至現在的秒數。所以這樣隨著時間的不同,生成的隨機數的個數也不同。
至於第二個程式中所有的數字都相同,那是因為本身迴圈並不多,每個迴圈時間間隔很近,所以實際上time(0)的值都一樣。這樣相當於每次都是取隨機序列中的第一個元素,那當然就一樣了。
上面提到了生成的隨機序列實際上是一個偽隨機序列,其實後面的數是根據前面的數經過複雜的變換得到的。我們可以來看看srand(int num)函式的大體實現原理。
#include <iostream>
#include <string.h>
#include <time.h>
#include <stdio.h>
#define RANDOM_MAX 0x7FFFFFFF
using namespace std;
static long do_rand(unsigned long *value)
{
/*
這個演算法保證所產生的值不會超過(2^31 - 1)
這裡(2^31 - 1)就是 0x7FFFFFFF。而 0x7FFFFFFF
等於127773 * (7^5) + 2836,7^5 = 16807。
整個演算法是通過:t = (7^5 * t) mod (2^31 - 1)
這個公式來計算隨機值,並且把這次得到的值,作為
下次計算的隨機種子值。
*/
long quotient, remainder, t;
quotient = *value / 127773L;
remainder = *value % 127773L;
t = 16807L * remainder - 2836L * quotient;
if (t <= 0)
t += 0x7FFFFFFFL;
return ((*value = t) % ((unsigned long)RANDOM_MAX + 1));
}
static unsigned long next = 1;
int rand(void)
{
return do_rand(&next);
}
void srand(unsigned int seed)
{
next = seed;
}
int main()
{
srand((unsigned)(time(NULL)));
for(int i=0;i<100; i++)
{
if(i % 10 == 0) puts("");
printf("%d\t",rand()%99+1);
}
return 0;
}
經過上面的分析,就很容易獲取一個0 ~ 1之間的一個隨機小數。因為隨機數中RAND_MAX最大,那麼就很容易了:
double get_rand()
{
return (double)rand() / (double)RAND_MAX;
}
在很多重要的演算法中,我們沒有用srand(int num)函式而直接用rand()來獲取隨機數,比如Miller素數測試,大數分解等等。這時實際上有一個預設的隨機種子,這個隨機序列是固定的。
那麼,Java中的隨機數又是怎麼樣的呢?
在java語言中生成隨機數的方法有三種:
1.通過System.currentTimeMillis()方法獲取當前時間的毫秒數(long);
2.通過Math.random()方法獲取一個介於0到1之間的偽隨機數;
3.通過Random類獲取隨機數;
在Random類中,使用了一個48位的種子數,並通過線性同於公式進行隨機數的生成(參考《The Art of Computer Programming, Volume 2》書的3.2.1節。
Random rand = new Random(System.currentTimeMillis());
在Java 中我們可以使用java.util.Random類來產生一個隨機數發生器。它有兩種形式的建構函式,分別是Random()和Random(long seed)。Random()使用當前時間即System.currentTimeMillis()作為發生器的種子,Random(long seed)使用指定的seed作為發生器的種子。
隨機數發生器(Random)物件產生以後,
通過呼叫不同的method:nextInt(),nextLong(),nextFloat(),nextDouble()等獲得不同型別隨機數。
生成隨機數
Random random = new Random();
Random random = new Random(100);//指定種子數100
random呼叫不同的方法,獲得隨機數。
import java.util.Random;
public class Srand {
public static void main(String[] args){
Random random = new Random(100);
System.out.println(random.nextInt());
System.out.println(random.nextFloat());
System.out.println(random.nextBoolean());
}
}
注意Java的Math類中也有一個random()方法,即Math.random()返回的是一個範圍為[0.0,1.0)之間的小數。
在Java中,下面的程式碼表示生成10個0到49之間的隨機數。
import java.util.Random;
public class Srand {
public static void main(String[] args){
Random random = new Random();
for(int i=0;i<10;i++){
System.out.println(random.nextInt(50));
}
}
}
在Python中,隨機數的生成需要先匯入random模組。
1.random.random()用於生成一個0到1的隨機符小數: 0 <= n < 1.0
>>> import random
>>> random.random()
0.9658869832707923
>>>
2.random.uniform的函式原型為:random.uniform(a, b),用於生成一個指定範圍內的隨機符點數,兩個引數一個是上限,一個是下限。如果a > b,則生成的隨機數n: a <= n <= b。如果 a<b, 則 b <= n <= a。
>>> random.uniform(1,10)
8.241539762435707
>>> random.uniform(10,1)
5.2752945576846075
>>>
3.random.randint()的函式原型為:random.randint(a, b),用於生成一個指定範圍內的整數。其中引數a是下限,引數b是上限,生成的隨機數n: a <= n <= b
>>> random.randint(1,10)
8
4.random.randrange的函式原型為:random.randrange([start], stop[, step]),從指定範圍內,按指定基數遞增的集合中
獲取一個隨機數。如:random.randrange(10, 100, 2),結果相當於從[10, 12, 14, 16, ... 96, 98]序列中獲取一個隨機數。random.randrange(10,
100, 2)在結果上與random.choice(range(10, 100,2) 等效。
>>> random.randrange(0,100,2)
20
5.random.choice從序列中獲取一個隨機元素。其函式原型為:random.choice(sequence)。引數sequence表示一個有序型別。這裡要說明 一下:sequence在python不是一種特定的型別,而是泛指一系列的型別。list,
tuple, 字元串都屬於sequence。
>>> random.choice('ACdreamers')
'e'
6.random.shuffle的函式原型為:random.shuffle(x[, random]),用於將一個列表中的元素打亂。
>>>
p = ['ACdreamers','Hello','World','NiHao']
>>> random.shuffle(p)
>>> print p
['World', 'Hello', 'ACdreamers', 'NiHao']
>>>
7.random.sample的函式原型為:random.sample(sequence, k),從指定序列中隨機獲取指定長度的片斷。sample函式不會修改原有序列。 如果k大於sequence元素個數的話會報錯。
>>> random.sample('ACdreamers',2)
['s', 'a']
>>>