高效產生一組不重複的隨機數
這個需求應該是很常見的吧,需要從 0 到 n 之間選
k 個不重複的數組成一個序列。
我最早遇到這個問題是在給校ACM比賽出題時,需要隨機產生一些測試資料,當時我想的是用一個輔助陣列記錄之前已經產生的隨機數,如果當前產生的隨機數已經出現過就再重新隨機。
顯然這樣的實現效率是很低的,設想從10000個數中隨機產生10000個數的序列,當前面9999個數已經確定了時,最後一個數被隨機到的概率是 0.0001,也就是說大概需要呼叫隨機函式10000次才會產生。類似的,第9999個數被隨機到的概率是0.0002……
.....
我後來採用了一個改進的辦法是,如果當前產生的隨機數a已經在之前產生過了,就順序去找比a小的數,直到找到一個之前沒有產生過的數,如果找不到就找比a大的數。
可以看到這樣的改進節省了大量的時間,但是這樣產生的已經不是隨機數序列了!
試想從1,2,3,4中隨機挑選2個數,假如第一次選出來的是3,那麼第二次再選的話,選中2的概率就變成了1/2,因為當隨機出來的數為2或3時,我們都選擇2。
在我遇到的應用中,因為對隨機數序列的“隨機性”要求不是很高,所以湊合著用了上述辦法。
直到今天在《Programming pearls》裡看到這個很完美的辦法:
for(i = 0; i < n; i++)
{
x[i] = i;
}
for(i = 0; i < k; i++)
{
t = rand(i,n-1);
swap(x[i], x[t]);
out(x[i]);
}
其中,rand(a,b)產生一個 a 到 b 之間的隨機數,swap(a,b)交換a和b的值,out(a)把a輸出作為結果。
我們來看看這個演算法的完美之處吧!
首先,x陣列裡把0到n-1的所有數都儲存了,而最後輸出的都是x陣列裡的值,所以滿足輸出的數是k個0到n-1的數。
然後,我們對於第 i 次隨機,產生一個 i 到 n-1 的下標 t ,並把x[t] 和x[i]交換,將其輸出,這樣每次產生的數都是之前沒有出現過的數,因為之前出現過的數都在x[0] 到 x[i-1]裡呢!這樣就保證了輸出資料的不重複性。
最後,我們考察輸出資料的“隨機性”,顯然,因為交換操作,使得所有沒有出現過的數都在x[i] 到 x[n-1]中存著呢,所以被選中的概率相等。
寫完上面這些文字之後,我在想,這樣經典的演算法,應該是早就已經出現了,但是我竟然還不知道,這樣看來,我百度實習面試遭鄙視也就是很自然的了,這也算是我之前的一個毛病,喜歡遇到問題才去想怎麼解決,沒問題就很少看相關的書或資料,而對於自己能解決的問題(比如上面說的這個湊合著能用的問題),我又懶得去找更好的甚或是標準的解決方法,所以才造成了我現在的知識侷限,以後要多看書,多想問題,儘量多的積累知識吧……
我最早遇到這個問題是在給校ACM比賽出題時,需要隨機產生一些測試資料,當時我想的是用一個輔助陣列記錄之前已經產生的隨機數,如果當前產生的隨機數已經出現過就再重新隨機。
顯然這樣的實現效率是很低的,設想從10000個數中隨機產生10000個數的序列,當前面9999個數已經確定了時,最後一個數被隨機到的概率是 0.0001,也就是說大概需要呼叫隨機函式10000次才會產生。類似的,第9999個數被隨機到的概率是0.0002……
.....
我後來採用了一個改進的辦法是,如果當前產生的隨機數a已經在之前產生過了,就順序去找比a小的數,直到找到一個之前沒有產生過的數,如果找不到就找比a大的數。
可以看到這樣的改進節省了大量的時間,但是這樣產生的已經不是隨機數序列了!
試想從1,2,3,4中隨機挑選2個數,假如第一次選出來的是3,那麼第二次再選的話,選中2的概率就變成了1/2,因為當隨機出來的數為2或3時,我們都選擇2。
在我遇到的應用中,因為對隨機數序列的“隨機性”要求不是很高,所以湊合著用了上述辦法。
直到今天在《Programming pearls》裡看到這個很完美的辦法:
for(i = 0; i < n; i++)
{
x[i] = i;
}
for(i = 0; i < k; i++)
{
t = rand(i,n-1);
swap(x[i], x[t]);
out(x[i]);
}
其中,rand(a,b)產生一個 a 到 b 之間的隨機數,swap(a,b)交換a和b的值,out(a)把a輸出作為結果。
我們來看看這個演算法的完美之處吧!
首先,x陣列裡把0到n-1的所有數都儲存了,而最後輸出的都是x陣列裡的值,所以滿足輸出的數是k個0到n-1的數。
然後,我們對於第 i 次隨機,產生一個 i 到 n-1 的下標 t ,並把x[t] 和x[i]交換,將其輸出,這樣每次產生的數都是之前沒有出現過的數,因為之前出現過的數都在x[0] 到 x[i-1]裡呢!這樣就保證了輸出資料的不重複性。
最後,我們考察輸出資料的“隨機性”,顯然,因為交換操作,使得所有沒有出現過的數都在x[i] 到 x[n-1]中存著呢,所以被選中的概率相等。
寫完上面這些文字之後,我在想,這樣經典的演算法,應該是早就已經出現了,但是我竟然還不知道,這樣看來,我百度實習面試遭鄙視也就是很自然的了,這也算是我之前的一個毛病,喜歡遇到問題才去想怎麼解決,沒問題就很少看相關的書或資料,而對於自己能解決的問題(比如上面說的這個湊合著能用的問題),我又懶得去找更好的甚或是標準的解決方法,所以才造成了我現在的知識侷限,以後要多看書,多想問題,儘量多的積累知識吧……
相關文章
- 高效產生不重複的隨機數隨機
- 高效產生一組不重複的隨機數(受程式設計珠磯啟示)java實現隨機程式設計Java
- 巧用物件,生成不重複隨機數物件隨機
- C++【生成16個不重複字母】(生成不重複隨機數)C++隨機
- JavaScript隨機不重複的字元組成新的字串JavaScript隨機字元字串
- 如何生成隨機不重複的11位數字隨機
- matlab之生成不重複的隨機整數Matlab隨機
- R產生隨機數隨機
- javascript生成不重複隨機數程式碼例項JavaScript隨機
- vc中產生隨機數隨機
- c++產生隨機數C++隨機
- 計算機隨機數的產生 (轉)計算機隨機
- 集合框架-產生10個1-20之間的隨機數,要求隨機數不能重複框架隨機
- JavaScript 陣列隨機不重複元素JavaScript陣列隨機
- 【筆記】如何產生隨機數筆記隨機
- js隨機產生區間數JS隨機
- 生成固定長度不重複的隨機字串隨機字串
- matlab產生隨機數或隨機矩陣Matlab隨機矩陣
- C語言程式生成指定範圍的不重複的隨機數C語言隨機
- js獲取指定位數不重複隨機數程式碼例項JS隨機
- 如何產生指定範圍的隨機數隨機
- C 語言產生隨機數的方法隨機
- C語言產生隨機數的方法C語言隨機
- oracle要對一個欄位產生隨機數Oracle隨機
- linux生產32位隨機數Linux隨機
- Math類產生隨機數後保留一位小數隨機
- 產生一個32位的16進位制隨機數隨機
- JavaScript陣列中隨機取出不重複項JavaScript陣列隨機
- 產生唯一隨機碼的方法分析隨機
- JavaScript產生隨機數例項程式碼JavaScript隨機
- 從oracle表中隨機取記錄,產生隨機數和隨機字串Oracle隨機字串
- 產生0到100內的任意隨機數隨機
- ORACLE產生隨機數的多種方法分享 轉Oracle隨機
- python怎麼生成隨機不重複陣列Python隨機陣列
- matlab中的產生隨機數的rand函式Matlab隨機函式
- Matlab產生隨機數函式小結Matlab隨機函式
- Rust中如何產生隨機數或密碼?Rust隨機密碼
- 利用arc4random_uniform()產生隨機數randomORM隨機