(PS:可能是我描述的不太清楚,其實意思就是這樣的。
依次產生10W個隨機數填充到數組,
隨機數1-10W之間,
不能重複。嗯,對,本意就是這樣。)
今天面試,技術總監給我出了個思考題,求隨機10W個數字無序排列,如何才能高效的執行,記憶體沒有要求,最好的硬體設定,虛擬碼如下:
static $arr = array();for($i = 0; $i < 100000; $i++){ $rand = rand(1,100000); $y = 0; while($rand == $arr[$y]) { $rand = rand(1,100000); $y++; } $arr[$i] = $rand;}
這樣做的壞處就是 剛開始取得第一個數的時候進入while迴圈的機率是1/100000,那麼當取到了第99999個數的時候,進入while迴圈的幾率就是99.9999%,那麼這時候就會出現無限迴圈狀態,那麼如何規避這個問題呢?
回複內容:
(PS:可能是我描述的不太清楚,其實意思就是這樣的。依次產生10W個隨機數填充到數組,隨機數1-10W之間,不能重複。嗯,對,本意就是這樣。)
今天面試,技術總監給我出了個思考題,求隨機10W個數字無序排列,如何才能高效的執行,記憶體沒有要求,最好的硬體設定,虛擬碼如下:
static $arr = array();for($i = 0; $i < 100000; $i++){ $rand = rand(1,100000); $y = 0; while($rand == $arr[$y]) { $rand = rand(1,100000); $y++; } $arr[$i] = $rand;}
這樣做的壞處就是 剛開始取得第一個數的時候進入while迴圈的機率是1/100000,那麼當取到了第99999個數的時候,進入while迴圈的幾率就是99.9999%,那麼這時候就會出現無限迴圈狀態,那麼如何規避這個問題呢?
UPDATE:既然題主明確了題意,我這裡也更新下:
產生10W個數,範圍[1..10w]且無重複,那麼只需要先在長度為10W的數組內填上1..10W,然後用下面的演算法shuffle就行。
這個實際上可以通過Fisher-Yates shuffle演算法來實現,漸進複雜度可以達到O(n)。描述如下:
void randomize(vector& data) { for (int i = data.size() - 1; i > 0; --i) { int random = rand() % (i + 1); // 所以 0 <= random <= i swap(data[random], data[i]); }}
要證明這個演算法的正確性也很簡單。但是需要將條件轉換成等價的形式,條件裡說我們需要對數組隨機排列,這意味著每個數出現在某個位子的幾率均等,均為1/n(假設有n個數)
我們考慮演算法執行第一次迴圈的時候:我們從[0,n-1]這n個數裡隨機挑選了一個數放到n-1這個位置,所以所有的數放到n-1的機率為1/n
當執行第二次的時候,我們從[0...n-2]這n-1個剩下的數裡選出了一個放到n-2這個位置,那麼出現在n-2位置的機率是多少呢?注意現在這件事並不是獨立事件了,一個數要放到n-2這裡意味著它沒有在第一次迭代中被選中,並且第二次被選中了,所以機率為(1-1/n)(1/(n-1)) = ((n-1)/n)(1(n-1)) = 1/n,故而所有數放在n-2的機率為1/n
一般的,一個數放在位置i時,意味著前面n-i-1次迴圈他都未被選中,且在第n-i次被選中,我們有機率p(i) = (1-1/n)(1-1/(n-1))(1-1/(n-2))....(1-1/(i+2))(1/(i+1) = ((n-1)/n) ((n-2)/(n-1)) ((n-3) / (n-2))...((i+1)/(n+2))(1/(i+1)) = 1/n
產生10W個數字,隨機遞增,再對結果集亂序
shuffle(range(0,100000));
步驟
1產生0-99999連續數組a
2空數組b
3隨機從a中取出一個放入b並從a中刪除該元素,直到取完
可以考慮使用 Format-preserving encryption
大致的目的就是格式不變的加密,在這個例子裡,可以把5位元加密成另一個5位元。
看到題目的第一個反應,真的只有我想到了C++的STL中的random_suffle
函數麼……
後來仔細看了一下題主用的是PHP(為啥貼的是C標籤啊……)
好吧,我們來個簡化版的。
結果一看,dahakawang已經給出實現了,好吧,就由我改成PHP文法吧~
$a=array();for($i=0;$i<100000;$i++) $a[$i]=$i+1;for($i=99999;$i>=0;$i--){ $j=rand(0,$i); $t=$a[$j]; $a[$j]=$a[$i]; $a[$i]=$t;}
直接手寫的,如有語法錯誤自行改正。
【隨機10W個數字無序排列】這其實沒說明問題...
如果解釋為【給10萬個隨機數做無序排列】,我覺得代碼只需要一行:return。
把10萬個數放在數組裡面,然後數組隨機順序就行了。好吧,我承認這麼搞根本符合題意。
下面的偽碼應該是期望的,不過貌似接近 @Dappur 的答案。
$arr=range(1, 100000, 1);//shuffle($arr); 哈哈,這是玩笑$results = [];for($i=99999;$i>=0;$i--){ $key=rand(0,$i); $results[]=$arr[$i]; array_slice($arr, $i, 1);}
shuffle: 打亂數組順序。
range: 建立區間數組,參數分別是最小值、最大值、步長。
array_slice: 取出數組中的一段,並重設下標。
重新@Dappur
想明白你的代碼了...相當於10萬個人,每個人舉著一個號,然後喊一聲跑,自然就亂了。
而像樓主說的那種逐個產生(就是不事先預定好)除了他自己寫的那種方式外則是無解的,就像10萬人都是隔離的,自由說一個數,還不能和別人重複,不可能。
高效是指快麼?
如果是,我會順序產生10W個數,開10W個線程隨機插入,如果位置已經被佔用就用就近原則找位置插入。
如果有10W個核的應該挺快的