簡體   English   中英

洗牌算法講解(java)

[英]Explanation of card shuffling algorithm (java)

我試圖在常見的 Java 洗牌算法中更好地理解這段代碼:

// Random for remaining positions. 
int r = i + rand.nextInt(52 - i); 

為什么需要“填充”或將i索引添加到結果隨機數中? 看起來,當您迭代並添加i ,通過減去i ,您可以將最大可能的隨機數范圍保持在 0 到 51 之間,但為什么不這樣做:

int r = rand.nextInt(52);

完整代碼:

      
    // Function which shuffle and print the array 
    public static void shuffle(int card[], int n) 
    { 
          
        Random rand = new Random(); 
          
        for (int i = 0; i < n; i++) 
        { 
            // Random for remaining positions. 
            int r = i + rand.nextInt(52 - i); 
              
             //swapping the elements 
             int temp = card[r]; 
             card[r] = card[i]; 
             card[i] = temp; 
               
        } 
    } 

Fisher-Yates shuffle 的工作原理如下:

  • 從數組中取出一個隨機元素,並將其交換到第一位
  • 剩余值中隨機抽取一個元素,並將其交換到第二位
  • 剩余值中隨機抽取一個元素,並將其交換到第三位
  • 等等

這是您要問的“剩余價值”部分。

例如,經過 10 次迭代后,您已將 10 個隨機值交換到數組的前 10 個位置,因此對於下一次迭代,您需要在范圍 10-end 中的隨機位置,因此從小於完整的 10 隨機范圍的偏移量 10范圍,又名i + rand.nextInt(52 - i)

其他答案沒有解決您的問題“為什么不直接做: int r = rand.nextInt(52); ”。 這有時被稱為 naive shuffle,答案是因為這會導致有偏差的 shuffle。

理想情況下,您希望結果的數量反映同樣可能結果所涉及的概率計算。 這意味着洗牌后的第一張牌可以是 52 張牌中的任意一張,第二張牌可以是剩余的 51 張牌中的任意一張,第三張牌可以是剩余的 50 張牌中的任意一張,……換句話說,有A = 52*51*50*...*3*2*1 (即,52 階乘)可能的卡片排列。 這就是 Fisher-Yates shuffle 及其變體所做的。 但是,如果您按照您的建議選擇任何一張牌在第 i迭代中移動,則會生成 C = 52 52 個案例。 結果是有偏見的洗牌。

為了說明在幼稚洗牌中偏差是如何以及為什么會出現的,請考慮一副小得多的 3 張牌。 在這種情況下,A = 3*2*1 = 6 個排列,而 C = 3 3 = 27 是到達最終排列的路徑數。 為什么這是個問題? 因為鴿子洞原理。 C 不是 A 的整數倍,所以如果我們把 C 看作鴿子,把 A 看作鴿子洞,那么某些洞一定比其他洞得到更多的鴿子。 用改組的術語來說,獲得某些安排的途徑比其他安排多。 因此,並非所有安排都會同樣頻繁地出現,因此結果不是“公平”的洗牌。

如果您通過分析來解決它並從初始化為['a','b','c']的數組開始,您將在使用 naive shuffle 時發現以下概率:

outcome         probability
-------         -----------
['a','b','c']       4/27
['a','c','b']       5/27
['b','a','c']       5/27
['b','c','a']       5/27
['c','a','b']       4/27
['c','b','a']       4/27

而通過無偏洗牌,6 種可能排列中每一種的概率為 1/6。 這就是創建 Fisher-Yates 算法的原因。

您的代碼所做的是創建牌組的洗牌。 在每次迭代中,它會隨機抽取一張牌並將其放回牌組的前面,從 i=0 到 i=n。

如果你會使用int r = rand.nextInt(52); 這意味着在每次迭代中,您都可以取回任何卡片,即使是在牌組開頭的那些已經是牌組新順序的一部分的牌。

通過減去 i 你只選擇剩下的 52-i 張牌中的一張牌,然后你需要加上 +i 以獲得它的實際位置,因為你已經將牌組開頭的前 i 張牌設置為新牌洗牌。

例如,假設您已經在新位置拿到了前 10 張卡片。 現在你需要拿到第 11 張牌,所以你從剩下的 52-10=42 張牌中隨機拿一張牌。 假設我們取回了數字 5,它不是card[4]的卡片,而是card[10+4]處的card[10+4]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM