轉 隨機數問題

2022-02-09 12:15:57 字數 3390 閱讀 6864

分類: programming pearls

data structure & algorithm

offer on the way

舉報目錄(?)

[+]最初問題:從n個數中隨機選擇m個數(0<=m<=n)。

為了便於描述,可以將該問題抽象為:從0-n-1這n個數中隨機選擇m個數。計算機能夠提供的隨機數都是偽隨機的,我們假設計算機提供的偽隨機數為真正的隨機。

系統(c/c++)提供的rand函式只有15位,如果不滿足要求,需要自己擴充套件,30位的隨機函式如下:

[cpp]view plain

copy

print

?/** @brief 返回乙個30bit的隨機數 

** @note   系統自帶的rand只有15bit 

*/  

int     bigrand()  

return  (rand()<<15)+rand();  

}  每次產生乙個0-n-1之間的隨機數,放入乙個集合中,直到集合的大小為m。c++的stl中有set,比較方便:

[cpp]view plain

copy

print

?void    getrandnum_set(int m,int n)  

set::iterator    i=s.begin();  

while(i!=s.end())  

cout<<*i++<<" ";  

cout<}  

上面的**工作沒有問題,但是當m接近n且很大時,最後幾個數的產生將會很困難。因為會生成大量的重複的數。

如何不產生重複的數呢?

假設當前剩餘m個數要選,

從0開始到n-1這n個數,以m/n的概率選中選中0:總共n個數,要選出m個;

對於1:如果選中0,則以(m-1)/(n-1)的概率選擇1(總共n-1個,要選m-1個);如果沒選中,則以m/(n-1)的概率選(總共n-1個,要選m個);

對於i:總共還剩下n-i個,還需要選m個,那麼選中的概率就是m/(n-i)。

沒選中乙個,剩餘要選的數就減少乙個。

因此**如下:

[cpp]view plain

copy

print

?/** @brief 在[0-n)中隨機的選擇m個不同的數 

**         並按序輸出 

*/  

void    getrandnumsorted(int m,int n)  

}  cout<}  

顯然,這時輸出是從小到大按序選擇的。

其中:if(bigrand()%(n-i)數 選中概率

0:  m/n

1: m/n * (m-1)/(n-1)  +  (1-m/n) * m/(n-1) =m/n;

2:    好多項相加,這裡就不寫了。。。

如果要求不按序輸出,有兩種解決辦法。

一種是將上面的結果儲存起來,然後再打亂儲存的陣列。

還有一種就是直接產生m個隨機數。

先看直接產生m個隨機數,其實就是先從0-n-1中隨機選擇乙個,作為第乙個;然後再從剩下的n-1個數中隨機選擇乙個作為第二個……直到選出第m個。這就是所謂「完美洗牌」或者打亂陣列。

[cpp]view plain

copy

print

?/** @brief 在[0-n)中隨機的選擇m個不同的數 

**         並隨機輸出 

*/  

void    getrandnum(int m, int n)  

cout}  這裡需要乙個函式,能夠隨機產生一定範圍內的數:

[cpp]view plain

copy

print

?/** @brief 返回[l,u]之間的乙個隨機數 **/  

int     randint(int l, int u)  

這種演算法的問題是,如果n很大,m很小,對輔助空間的浪費太嚴重。因為開闢了那麼大的空間,實質只用了很少一部分。

另一種就是先按序隨機選擇m個數,然後再打亂:

[cpp]view plain

copy

print

?/** @brief 在[0-n)中隨機的選擇m個不同的數 

**         並隨機輸出 

*/  

void    getrandnum2(int m, int n)  

}  for(int i=0; icout}  在不知道檔案總行數的情況下,隨機讀取檔案中的一行。

最直觀的做法就是,先讀取一次檔案,確定總行數n。然後產生乙個1-n的隨機數m,再讀取第m行。顯然這是可行的,但是問題是如果檔案很大,平均要遍歷檔案1.5次。效率很低。

而且如果檔案在不算增長,那麼這個方法就不行了。

通過上面的演算法的啟發,其實也可以只讀取一次。

首先讀取第一行,如果只有一行,就結束了,設為line;

如果有第2行,那麼以1/2的概率替換line;這時1、2兩行被選中的概率都是1/2.

如果有第3行,那麼以1/3的概率替line;則第3行被選中的概率是1/3,1、2兩行被選中的概率則都是1/2*2/3=1/3.

第i行,以1/i的概率替換line。

直到檔案結束。

[cpp]view plain

copy

print

?/** @brief 從檔案fname中隨機讀取一行 */  

void    getonelinerand(const char *fname)  

else  

cout<" : "<

cnt++;  

}  cout<<"rand line : "<

ins.close();  

}  這裡的if(randint(1,cnt)==1)裡的1,可以是[1,cnt]中任意乙個值,概率均為1/cnt。先去讀k行,儲存在乙個陣列中(假設檔案至少有k行);

然後每讀取一行,都以k/n的概率替換陣列中的任意一行,其中n為當前總共讀取的行數。

[cpp]view plain

copy

print

?/** @brief 從檔案fname中隨機讀取k行 

*/  

void    getrandlines(const char *fname, int k)  

while(getline(ins,line))  

cnt++;  

}  for(int i=0; i

coutins.close();  

}  其他問題請參考《程式設計珠璣-第12章》。

隨機數 簡學 隨機數問題

隨機是概率論用語,指事前不可預言的,即在相同條件下重複進行試驗,每次結果未必相同,或知道事物過去的狀況,但未來的發展卻不能完全肯定,隨機也有 真隨機 與 偽隨機 真隨機 比如量子力學,因為乙個放射性物質原子下一分鐘是否發生衰變是真正隨機的,不可預言的 偽隨機 由經過系統的不同的因素造成,或是由於忽略...

隨機數問題

要產生真正的隨機數還是有難度的!好在vc中提供有隨機數函式,rand 函式。msdn中定義如下 include include include void main void int i seed the random number generator with current time so tha...

隨機數問題

1 隨機數問題 希望掉落2或者4。1 使用std time 函式取得乙個每次都不一樣的時間作為種子數值 2 使用std srand 函式設定這個種子數值初始化隨機數發生器 3 使用std rand 函式根據我們所需要縮放的範圍進行取餘處理製造我們所需要的隨機數 1.1std time 函式 傳入nu...