生成隨機數的方式你選對了嗎?

來源:公衆號【編程珠璣】

作者:守望先生

ID:shouwangxiansheng

 

生成隨機數的N種方式

首先需要說明的是,計算機中生成的隨機數嚴格來說都是僞隨機,即非真正的隨機數,真正隨機數的隨機樣本不可重現。那麼我們來看看代碼中有哪些方式可以生成隨機數。

rand

rand函數聲明如下:

#include <stdlib.h>
int rand(void);

​rand函數返回[0,RAND_MAX)範圍的隨機整數,在我的機器上,RAND_MAX爲2147483647。
使用示例:

/*來源:公衆號【編程珠璣】
rand.c
*/
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
    int i = 0;
    while(i < 5)
    {
        printf("%d ",rand());
        i++;
     }
    printf("\n");
    return 0;
}

編譯運行:

$ gcc -o rand rand.c
./rand
1804289383 846930886 1681692777 1714636915 1957747793

多運行幾次,你就會驚喜地發現,每次運行的結果都是一樣的!!!這還玩個毛線?

srand

別急,rand雖然每次運行的結果都是一樣的,那是因爲它的種子默認爲1。每一個種子會有一串看似隨機的序列,每次取下一個出來,整體都近乎是隨機分佈的。但是如果你的種子每次都是一樣的,那麼每次運行可能得到的結果也是一樣的。我們需要利用srand給它一個種子。

#include <stdlib.h>
void srand(unsigned int seed);

爲了保證我們每次的得到的隨機數不一樣,我們必須在每次調用時,都確保種子不一樣,因此通常會選擇使用時間作爲種子,注意這只是通常的種子選擇,你可以根據實際使用需求進行選擇。

於是我們在使用之前設置好種子,使用示例:

/*來源:公衆號【編程珠璣】
rand.c
*/
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
int main(void)
{
    srand(time(NULL));//設置隨機種子,注意只需要設置一次即可
    int i = 0;
    while(i < 5)//生成5個隨機數
    {
        printf("%d ",rand());
        i++;
     }
    printf("\n");
    return 0;
}

現在好了,每次運行生成的都不一樣了。但是還有一個問題,如果這種方式在多線程下使用,也是不可取的,因爲rand不是可重入函數。它的每次調用都會修改一些隱藏的屬性,因此在多線程中使用它並不合適。

rand_r

爲了在多線程下使用,我們使用rand_r,使用方式和rand是一樣的:

#include <stdlib.h>
int rand_r(unsigned int *seedp);

使用示例:

//來源:公衆號【編程珠璣】
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
int main(void)
{
    unsigned int seed = time(NULL);
    int i = 0;
    while(i < 5)//生成5個隨機數
    {
        printf("%d ",rand_r(&seed));
        i++;
    }
    printf("\n");
    return 0;
}

多線程中,多個線程可能幾乎同時調用,那它們的種子可能也一樣,如果想不一樣,還可以將種子設置成和線程id有關。

unsigned int seed  = time(NULL)^pthread_self(); 

random

通過前面的例子可以發現,rand生成的整數範圍是有限的,爲了生成更大範圍,可以使用random:

#include <stdlib.h>
long int random(void);
void srandom(unsigned int seed);

random返回的類型爲long int,因此在一定程度上,它生成的範圍要大得多。另外與rand類似,需要使用srandom函數設置種子。具體的例子就不再放出了。

生成指定範圍隨機數

前面的例子都是生成[1,RAND_MAX]之間的數,如果要生成指定區間的隨機數呢?假設a和b不超過int範圍以及它們的差值不超過rand的生成範圍。

[a,b)

左閉右開區間,即包含a,不包含:

 (rand() % (b - a)) + a;

[a,b]

左閉右閉,即包含a和b:

 (rand() % (b - a + 1)) + a;

(a,b]

左開右閉,即不包含a,包含b:

(rand() % (b-a)) + a + 1;

[0,b]

rand() % b ;

0到1之間的浮點數

rand()/(double)RAND_MAX;

舉例

生成[2,10)之間的隨機數5個:

//來源:公衆號【編程珠璣】
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
int main(void)
{
    srand(time(NULL));//設置隨機種子,注意只需要設置一次即可
    int i = 0;
    int a = 2;
    int b = 10;
    while(i < 5)//生成5個隨機數
    {
        printf("%d ",( rand() % ( b - a ) )+ a);
        i++;
     }
    printf("\n");
    return 0;
}

總結

記住,通過這些方法生成的都是僞隨機數。而一個好的隨機算法,它的隨機性很強,可能需要根據使用場景去設計具體的算法。本文所介紹的僅僅是庫函數提供的隨機數生成函數。

相關精彩推薦

系統編程-文件讀寫這件小事

我有一個問題,用了多線程後,兩個問題有了現在

 

關注公衆號【編程珠璣】,獲取更多Linux/C/C++/數據結構與算法/計算機基礎/工具等原創技術文章。後臺免費獲取經典電子書和視頻資源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章