洗牌算法一:
生成一個不重複的隨機序列,將隨機序列綁定到nums[],而後對隨機序列作一次排序。java
洗牌算法二:(經典洗牌算法)算法
for(int i=nums.length-1; i>=1; i--) Swap(nums[i], nums[rand()%(i+1)]);
經典算法的證實:數組
對於nums[i],洗牌後在第n-1個位置的機率是1/n(第一次交換的隨機數爲i)
在n-2個位置機率是[(n-1)/n] * [1/(n-1)] = 1/n,(第一次交換的隨機數不爲i,第二次爲nums[i]所在的位置(注意,若i=n-1,第一交換nums[n-1]會被換到一個隨機的位置))
在第n-k個位置的機率是[(n-1)/n] * [(n-2)/(n-1)] *...* [(n-k+1)/(n-k+2)] *[1/(n-k+1)] = 1/n
(第一個隨機數不要爲i,第二次不爲nums[i]所在的位置(隨着交換有可能會變)……第n-k次爲nums[i]所在的位置)ide
洗牌算法三:(inside-out算法,可用於未知牌數)對象
相似於蓄水池抽樣算法。blog
int i=0; while(nums[i]存在) { int k = rand()%(i + 1); res[i] = res[k]; res[k] = nums[i++];
}
上面是僞代碼,若是知道nums的lenght的話,能夠改成for循環,因爲是從前日後遍歷,因此能夠應對nums[]數目未知的狀況,或者nums[]是一個動態增長的狀況。排序
證實以下:for循環
原數組的第 i 個元素在新數組的前 i 個位置的機率都是:(1/i) * [i/(i+1)] * [(i+1)/(i+2)] *...* [(n-1)/n] = 1/n,(即第i次恰好隨機放到了該位置,在後面的n-i 次選擇中該數字不被選中)class
原數組的第 i 個元素在新數組的 i+1 (包括i + 1)之後的位置(假設是第k個位置)的機率是:(1/k) * [k/(k+1)] * [(k+1)/(k+2)] *...* [(n-1)/n] = 1/n(即第k次恰好隨機放到了該位置,在後面的n-k次選擇中該數字不被選中)隨機數
蓄水池抽樣:
問題:如何隨機從n個對象中選擇一個對象,這n個對象是按序排列的,可是在此以前你是不知道n的值的。
解法:咱們老是選擇第一個對象,以1/2的機率選擇第二個,以1/3的機率選擇第三個,以此類推,以1/m的機率選擇第m個對象。當該過程結束時,每個對象具備相同的選中機率,即1/n.