天天看點

随機數:真随機數和僞随機數I.真随機數&僞随機數的基本定義II.c語言中的僞随機數詳解III.真随機數的詳解

說到随機這個詞,相信各位肯定都深有體會了。生活中有太多的不确定因素從各方各面影響着我們,但也正是因為這樣我們的人生更加多彩,具有了更多的可能性。

可以說,随機是個非常有魅力的東西。

你在生活中可能因為随機享過福,也有可能吃過虧。想要對它了解更多?如今是時候去揭開它的真面目了。

I.真随機數&僞随機數的基本定義

在這之前需要先明白一點:随機數都是由随機數生成器(Random Number Generator)生成的。

1.真随機數 TRUE Random Number

真正的随機數是使用實體現象産生的:比如擲錢币、骰子、轉輪、使用電子元件的噪音、核裂變等等,這樣的随機數發生器叫做實體性随機數發生器,它們的缺點是技術要求比較高。      ----百度百科

根據百科上的定義可以看到,真随機數是依賴于實體随機數生成器的。使用較多的就是電子元件中的噪音等較為進階、複雜的實體過程來生成。

至于“宇宙中不存在真正的随機”這種言論已經屬于哲學範疇,在此不做讨論。在此我們預設存在随機。

使用實體性随機數發生器生成的真随機數,可以說是完美再現了生活中的真正的“随機”,也可以稱為絕對的公平。

2.僞随機數 Pseudo-Random Number

真正意義上的随機數(或者随機事件)在某次産生過程中是按照實驗過程中表現的分布機率随機産生的,其結果是不可預測的,是不可見的。而計算機中的随機函數是按照一定算法模拟産生的,其結果是确定的,是可見的。我們可以這樣認為這個可預見的結果其出現的機率是100%。是以用計算機随機函數所産生的“随機數”并不随機,是僞随機數。

---百度百科

從定義我們可以了解到,僞随機數其實是有規律的。隻不過這個規律周期比較長,但還是可以預測的。主要原因就是僞随機數是計算機使用算法模拟出來的,這個過程并不涉及到實體過程,是以自然不可能具有真随機數的特性。

II.c語言中的僞随機數詳解

既然我們已經了解了真僞随機數的概念,接下來就來探究一下離我們最近的僞随機數吧。

c語言中就存在一個随機函數:rand().它就是一個标準的僞随機數生成器。依賴的頭檔案是stdlib.h.

接下來我就使用rand函數,來試着産生一些随機數。

#include <stdio.h>
#include <stdlib.h>

int main ()
{
       int random = rand();
    printf("%d\n",random);
    random = rand();
    printf("%d\n",random);
    random = rand();
    printf("%d\n",random);
}
           

這裡我使用一個整形變量來接受rand函數産生的随機數,并對其進行列印。

随機數:真随機數和僞随機數I.真随機數&amp;僞随機數的基本定義II.c語言中的僞随機數詳解III.真随機數的詳解

執行後,成功看到了結果。貌似是一串随機數。

可是,當我們多次執行時,發現它的數值卻還是41,18467, 6334。

這并不是我們想要的結果,我們希望每一次運作都可以産生不同的數值。可是如何解決這個問題呢?

其實,在c語言中的rand函數中有一個定義叫做種子,rand函數是通過對這個種子進行一系列的運算來模拟出一個随機數的。我們直接調用rand函數,并不指定種子,系統就會調用預設的種子:1,來産生随機數。因為每次重新運作時的種子都是1,運作出來的結果自然就還是這幾個數啦。

現在我們已經知道了種子這個重要的參數,我們就可以用一個一進制二次方程來模拟這個過程。

例如,rand函數的内部實作為 x^2 + 4x - 8,這個種子就相當于x。

如果想要改變rand最終的值,隻能通過改變x來實作。

那麼,既然要産生随機數,x就應該是一直在不斷變化的,才能讓x^2 + 4x - 8這個表達式的值不斷的變化。

x,也就是種子,該采用什麼東西呢?

沒錯,最理想的就是時間了。時間每分每秒都在變動,正好符合我們的要求。這裡需要使用到time.h。

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main ()
{
	srand ((unsigned)time(NULL));
	int random = rand();
	printf("%d\n",random);
	random = rand();
	printf("%d\n",random);
	random = rand();
	printf("%d\n",random);
}
           

對之前的代碼進行改進。通過時間設定種子的值:srand ((unsigned)time(NULL));

這時候再運作,每次都會得出不同的結果了。

這裡srand函數的定義就是:随機數生成器的初始化函數。通常是和rand函數配合使用的。

函數原型:srand(unsigned seed)

代碼中的time(NULL)将傳回計算機目前的時刻與1970年1月1日0時0分0秒之間的時間差,機關是秒。

srand又需要unsigned int類型,是以這裡使用一個強制類型轉換,最後完成種子的設定。

需要注意的是,srand函數必須放在循環或者循環調用的外面,否則還是會得出重複的數字。

到這裡,就可以産生你想要的随機數了。如果你想在0~4之間産生随機數,隻需要int random = rand()%4,這樣每次出來的随機數就會在0-4中了。

那麼,我想讓産生的随機數在1-100範圍内,用int random = rand()%100,這樣行嗎?

當然不行!!

是,沒錯,結果都是在範圍内的。但是你如果短時間内連續執行,會發現它是有規律可循的,會随着時間的推移慢慢上漲,到100後再回到0,再重新上漲...

這可是個嚴重的問題。遊戲如果敢這麼做,肯定會虧的妻離子散。

是以,這個辦法隻能用于範圍較小的随機數,應用範圍非常窄。

通用的一個方法是  (int)(n*rand()/(RAND_MAX+1.0)),這樣産生随機數的周期會大大縮短,進而達到我們想要的效果。

III.真随機數的詳解

之前已經介紹過,真随機數是使用實體裝置産生的。那麼在這裡我就要介紹一個網站:

真随機數生成網站

這個網站可以免費提供真随機數的服務,并且可以自己設定上下限,通常用于重要場合。

那麼,既然僞随機數生成那麼簡單,而且看上去确實是随機的,為什麼人們還要大費周章的使用繁瑣又高價的實體裝置去獲得随機數呢?

前面在僞随機數的定義裡講了,僞随機數其實是有周期的。

聽起來很恐怖對不對?也就是說,經過足夠多次的運作,結果會出現重複。

// Requires the GD Library
header("Content-type: image/png");
$im = imagecreatetruecolor(512, 512)
    or die("Cannot Initialize new GD image stream");
$white = imagecolorallocate($im, 255, 255, 255);
for ($y = 0; $y < 512; $y++) {
    for ($x = 0; $x < 512; $x++) {
        if (rand(0, 1)) {
            imagesetpixel($im, $x, $y, $white);
        }
    }
}		
imagepng($im);
imagedestroy($im);
           

這是摘自

Pseudo-Random Num.vs True Random Num

的一段代碼,使用PHP語言編寫的。它的作用就是将随機數可視化。下面分别放出真随機數和僞随機數的圖像。

真随機數圖像:

随機數:真随機數和僞随機數I.真随機數&amp;僞随機數的基本定義II.c語言中的僞随機數詳解III.真随機數的詳解

僞随機數圖像:

随機數:真随機數和僞随機數I.真随機數&amp;僞随機數的基本定義II.c語言中的僞随機數詳解III.真随機數的詳解

很明顯的可以看到,僞随機數的圖像呈現出了某種規律。但作者也同時解釋到:這個現象也隻是在Windows平台上的php語言中的 rand函數裡出現。當他在linux上運作同樣的代碼時,發現并沒有非常明顯的規律。同樣是windows平台的PHP語言,使用mt_rand()這個改進了的随機數生成函數的話也不會發現明顯的規律。

筆者推測是大大增加了重複的周期,畢竟是算法産生的僞随機數,永遠不可能具有真随機數的不确定性。也就是說,規律還是存在,隻不過需要更長的觀察周期 才能夠發現而已。

是以真随機數的重要性就在于,完全沒有規律。是以一般企業對産品的加密秘鑰的生成必須采用真随機數生成器,這樣才能保證萬無一失,杜絕了被破解的可能性。

繼續閱讀