卡改组算法的说明java

问题描述

我试图通过通用的Java shuffle纸牌算法来更好地理解这段代码

// 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端范围内的随机位置,因此与10个随机范围的偏移量比全范围小10个,也称为i + rand.nextInt(52 - i)

,

其他答案未解决您的“ 为什么不这样做: int r = rand.nextInt(52);”。这有时被称为“天真洗牌”,答案是因为这会导致有偏的洗牌。

理想情况下,您希望结果的数量能够反映同样可能的结果所涉及的概率计算。这意味着洗牌后的第一张牌可以是52张牌中的任何一张,第二张可以是剩余的51张牌中的任意一张,第三张可以是剩余的50张牌中的任意一张,...换句话说,有A = 52*51*50*...*3*2*1(即52个阶乘)卡的可能排列。这就是Fisher-Yates改组及其变体所做的事情。但是,如果您按照建议的方式选择任何一张卡进行第i 次迭代,则会生成C = 52 52 个案例。结果是有偏差的洗牌。

为说明天真混洗中偏见的产生方式和原因,请考虑使用3张卡片的小得多的牌组。在这种情况下,A = {3*2*1 = 6个排列,而C = 3 3 = 27是到达最终排列的路径数。为什么这是个问题?由于鸽子洞原理。 C不是A的整数倍,因此,如果我们将C视为鸽子,而将A视为鸽子洞,则某些 洞必须比其他洞得到更多的鸽子。用改组的术语来说,达成某些协议的途径比其他途径多。因此,并非所有安排都会同样频繁地发生,因此结果不是“公平”的洗牌。

如果您进行分析得出结论并从初始化为['a','b','c']的数组开始,则使用朴素的随机播放时,您会发现以下几率:

outcome         probability
-------         -----------
['a','c']       4/27
['a','c','b']       5/27
['b','a','c']       5/27
['b','a']       5/27
['c','b']       4/27
['c','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]

上的卡