Linux下隨機數生成的函式與常見方法
標頭檔案
#include<stdlib.h>
定義函式
int rand(void)
函式說明
rand()會返回一隨機數值,範圍在0至RAND_MAX 間。在呼叫此函式產生隨機數前,必須先利用srand()設好隨機數種子,如果未設隨機數種子,rand()在呼叫時會自動設隨機數種子為1。關於隨機數種子請參考srand()。
返回值
返回0至RAND_MAX之間的隨機數值,RAND_MAX定義在stdlib.h,其值為2147483647。
srand函式:
標頭檔案
#include<stdlib.h>
定義函式
void srand (unsigned int seed);
函式說明
srand()用來設定rand()產生隨機數時的隨機數種子。引數seed必須是個整數,通常可以利用geypid()或time(0)的返回值來當做seed。如果每次seed都設相同值,rand()所產生的隨機數值每次就會一樣。
用法:
要想每次執行得到的隨機數不同,我們還要設定隨機數種子。
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int r(int fanwei)
{
srand((unsigned)time(NULL)); //用於保證是隨機數
return rand()%fanwei; //用rand產生隨機數並設定範圍
}
int main()
{
cout<<r(100)<<endl; //生成100以內的隨機數,並顯示
return 0;
}
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(int argc, char *argv[]) { int i; srand((int) time(0)); for(i=0;i< 10;i++) { printf(" %d ", rand()); } printf("n"); return 0; }
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[])
{
int i, nu;
srand((int) time(0));
for(i=0;i< 10;i++)
{
nu = 0 + (int)( 26.0 *rand()/(RAND_MAX + 1.0));
printf(" %d ", nu);
}
printf("n");
return 0;
}
以下是不使用函式進行生成隨機數
眾所周知,利用Linux下的rand函式可以生成範圍在0到RAND_MAX(在stdlib.h中定義,值為2147483647)的數值,但是一般來講,為了達到更好的隨機效果,需要利用srand函式設定相應的隨機種子(或者說隨機數的起始值),種子相同,所產生的隨機數也是相同的,因此,要想獲得隨機效果好的隨機數,一定要保證每次的隨機種子有差別。常見的可作為隨機種子的有:當前時間、/dev/random或/dev/urandom檔案內容以及uuid碼。
在闡述各種隨機種子產生之前,有必要先介紹下srand函式,它是ISO C下的產生隨機數的標準函式,BSD下與之相對應的函式是random。srand函式需要接收的引數是一個無符號整型資料。
一、當前時間:
利用time函式每次獲取當前的系統時間,並且也保證了每次的隨機種子有差異,實驗證明它是一種最為簡介,同時也是使用最為廣泛的方法,但是,針對某些特定環境,未必會產生有效的隨機種子。在本篇日誌中,我們試圖將這三種產生隨機種子的方法應用於這樣一種特殊環境:在嵌入式環境下,試圖在開發板啟動之後、系統時間沒有同步之前這段時間去產生隨機種子,並檢驗所產生的隨機種子的有效性,值得說明的是,此種開發板不會去儲存系統時間,每次啟動之後都會從一個預設時間開始計時。通過實驗得出,當前時間運用在這種環境下完全不能達到產生隨意隨機數的目的,因為嵌入式開發板的特殊性,在每次開發板執行到產生隨機數的程式時,此時的當前時間都是一樣的,於是每次重新啟動開發板所產生的隨機數也是一樣的。
二、/dev/random以及/dev/urandom:
/dev/random以及/dev/urandom產生隨機種子的原理是利用當前系統的熵池來計算出一定數量的隨機位元,其中熵池是根據當前系統的“環境噪音”,它是由很多引數共同評估的,如記憶體的使用,檔案使用量等等,環境噪音直接影響著所產生的隨機種子的有效性。同樣,若我們試圖運用於前面所說的特殊環境,是否能夠產生滿足所需的隨機種子呢?實驗證明,能夠保證每次開發板在每次啟動之後能獲取到不同的隨機數。畢竟環境噪音所選取的評估引數眾多,會導致所獲取到的種子有很大的差異性。
下面試圖闡述一下/dev/random
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *fs_p = NULL;
unsigned int seed = 0;
fs_p = fopen ("/dev/urandom", "r");
if (NULL == fs_p)
{
printf("Can not open /dev/urandom\n");
return -1;
}
else
{
fread(&seed, sizeof(int), 1, fs_p); //obtain one unsigned int data
fclose(fs_p);
}
srand(seed);
printf("Random numner is %u.\n", rand());
return 0;
}
三、UUID碼: uuid碼的全程是通用唯一識別碼,它是一個軟體建構的標準,設定uuid碼的目的是讓分散式系統中的每一個元素都有唯一識別的資訊,它是由32個16進位制資料組成,並用”-”連線號分成五段,形成8-4-4-4-12形式的資料,理論上來講,uuid的總數是2128。Linux下的uuid碼是由核心提供的,存在/proc/sys/kernel/random/uuid檔案裡,從維基百科瞭解到,生成uuid的方法眾多,至今出現了5個版本,MAC地址、DCE加密、MD5 hash、隨機數機制以及SHA-1 hash(最新版本5,在RFC4122中有闡述)。既然我們已經獲取到了唯一的uuid碼,那如何轉換成我們實際srand函式所需的無符號整數呢?
可以採取的措施有:從/proc/sys/kernel/random/uuid檔案的CRC校驗碼中抽取最多10位,並且小於2147483647的資料;直接抽取檔案內容中的數值位,但要保證其最多10位,並且值小於2147483647.下面程式碼模擬了後者:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define FILENAME "/proc/sys/kernel/random/uuid"
#define MAX_SEED 10
#define UUID_LENGTH 35
#define UUID_DELIM "-"
void check_rand_number(char *seed, char *p)
{
char *tmp_seed = "2147483647"; //MAX_RAND
char *tmp = seed;
//fill the new number to seed array
*(seed + strlen(seed)) = *p;
if (strcmp(tmp_seed, tmp) >= 0)
{
return ;
}
else
{
*(seed + strlen(seed) - 1) = '\0';
}
}
void parse_uuid_and_save(FILE *fs_p, char *seed)
{
char uuid[UUID_LENGTH + 1] = {0};
char *p = NULL;
//UUID format like as "2946b341-55b0-44c6-9710-04c947d1e3e1"
if (NULL != fgets(uuid, UUID_LENGTH, fs_p))
{
if ((p = strtok(uuid, UUID_DELIM)) != NULL)
{
while(*p != '\0')
{
if (*p >= '0' && *p <= '9')
{
*(seed + strlen(seed)) = *p;
p++;
}
else
{
p++;
}
}
}
else
{
return;
}
while ( (p = strtok(NULL, UUID_DELIM)) != NULL)
{
while((*p != '\0') && (strlen(seed) < MAX_SEED))
{
if (*p >= '0' && *p <= '9')
{
//when add the tenth bit, should check the number must less than RAND_MAX(2147483647)
if (strlen(seed) == (MAX_SEED - 1))
{
check_rand_number(seed, p);
return;
}
else
{
*(seed + strlen(seed)) = *p;
p++;
}
}
else
{
p++;
}
}
}
}
}
int main(int argc, char **argv)
{
FILE *fs_p = NULL;
char seed[MAX_SEED + 1] = {0};
fs_p = fopen (FILENAME, "r");
if (NULL == fs_p)
{
printf("Can not open UUID file!\n");
return -1;
}
else
{
parse_uuid_and_save(fs_p, seed);
fclose(fs_p);
}
srand(strtol(seed, NULL, 10));
printf("Random number is %d.\n", rand());
return 0;
}