1. 程式人生 > >關於C++,Java和Python中的隨機數生成法

關於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)函式是一個偽隨機數序列,序列中的每一個數是由對

其前面的數字進行復雜變換得到的。為了模模擬正的隨機性,首先要呼叫srand()函式給序列設定一個種子。為了更好地滿足隨機性,使用了時間函式time(),以便取到一個隨時間變化的值,使每次執行rand()函式時從srand()函式所得到的種子值不相同。偽隨機數生成器將作為 "種子 "的數當作初始整數傳給函式。這粒種子會使這個球(生成偽隨機數)一直滾下去。 

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']

>>>