洗牌算法


洗牌算法一:
生成一個不重復的隨機序列,將隨機序列綁定到nums[],然后對隨機序列做一次排序。


洗牌算法二:(經典洗牌算法)

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]所在的位置)

洗牌算法三:(inside-out算法,可用於未知牌數)

類似於蓄水池抽樣算法。

int i=0;
while(nums[i]存在)
{
	int k = rand()%(i + 1);
	res[i] = res[k];
	res[k] = nums[i++];
}

上面是偽代碼,如果知道nums的lenght的話,可以改為for循環,由於是從前往后遍歷,所以可以應對nums[]數目未知的情況,或者nums[]是一個動態增加的情況。

證明如下:

原數組的第 i 個元素在新數組的前 i 個位置的概率都是:(1/i) * [i/(i+1)] * [(i+1)/(i+2)] *...* [(n-1)/n] = 1/n,(即第i次剛好隨機放到了該位置,在后面的n-i 次選擇中該數字不被選中)

原數組的第 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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM