你从这个破碎的随机洗牌中得到了什么分布?

时间:2011-02-27 03:51:06

标签: algorithm language-agnostic math random shuffle

着名的Fisher-Yates shuffle算法可用于随机置换长度为N的数组A:

For k = 1 to N
    Pick a random integer j from k to N
    Swap A[k] and A[j]

我一再被告知的一个常见错误就是:

For k = 1 to N
    Pick a random integer j from 1 to N
    Swap A[k] and A[j]

也就是说,不是从k到N中选择一个随机整数,而是从1到N中选择一个随机整数。

如果你犯了这个错误怎么办?我知道由此产生的排列不是均匀分布的,但我不知道对于最终的分布有什么保证。特别是,是否有人有关于元素最终位置的概率分布的表达式?

10 个答案:

答案 0 :(得分:55)

经验方法。

让我们在Mathematica中实现错误的算法:

p = 10; (* Range *)
s = {}
For[l = 1, l <= 30000, l++, (*Iterations*)
   a = Range[p];
   For[k = 1, k <= p, k++, 
     i = RandomInteger[{1, p}];
     temp = a[[k]];
     a[[k]] = a[[i]];
     a[[i]] = temp
   ];
   AppendTo[s, a];
]  

现在获取每个整数在每个位置的次数:

r = SortBy[#, #[[1]] &] & /@ Tally /@ Transpose[s]  

让我们在结果数组中取三个位置,并绘制该位置中每个整数的频率分布:

对于位置1,频率分布为:

enter image description here

对于位置5(中间)

enter image description here

对于位置10(最后):

enter image description here

在这里你可以得到所有位置的分布:

enter image description here

您可以在8个位置获得更好的统计数据:

enter image description here

一些观察结果:

  • 对于所有职位的概率 “1”是相同的(1 / n)。
  • 概率矩阵是对称的 关于大反对角
  • 那么,最后一个数字的概率 位置也是统一的(1 / n)

您可以将这些属性可视化,查看同一点(第一个属性)和最后一个水平线(第三个属性)的所有行的起始位置。

第二个属性可以从以下矩阵表示示例中看出,其中行是位置,列是占用者编号,颜色代表实验概率:

enter image description here

对于100x100矩阵:

enter image description here

修改

为了好玩,我计算了第二个对角线元素的确切公式(第一个是1 / n)。其余的可以完成,但这是很多工作。

h[n_] := (n-1)/n^2 + (n-1)^(n-2) n^(-n)

从n = 3到6验证的值({8 / 27,57 / 256,564 / 3125,7105 / 46656})

修改

在@wnoise答案中进行一些明确的计算,我们可以获得更多信息。

用p [n]替换1 / n,所以计算保持未评估,例如我们得到矩阵的第一部分,其中n = 7(点击查看更大的图像):

enter image description here

在与n的其他值的结果进行比较之后,让我们识别矩阵中的一些已知整数序列:

{{  1/n,    1/n      , ...},
 {... .., A007318, ....},
 {... .., ... ..., ..},
 ... ....,
 {A129687, ... ... ... ... ... ... ..},
 {A131084, A028326 ... ... ... ... ..},
 {A028326, A131084 , A129687 ... ....}}

您可以在精彩的http://oeis.org/

中找到这些序列(在某些情况下具有不同的符号)

解决一般问题比较困难,但我希望这是一个开始

答案 1 :(得分:28)

你提到的“常见错误”是通过随机换位进行改组。 Diaconis和Shahshahani在Generating a random permutation with random transpositions (1981)中详细研究了这个问题。他们对停止时间和趋同性进行了全面分析。如果您无法获得该论文的链接,请发送电子邮件给我,我可以转发给您。这实际上是一个有趣的阅读(就像Persi Diaconis的大部分论文一样)。

如果阵列重复输入,则问题略有不同。作为一个无耻的插件,这个更普遍的问题由我自己,Diaconis和Soundararajan在A Rule of Thumb for Riffle Shuffling (2011)的附录B中解决。

答案 2 :(得分:15)

让我们说

  • a = 1/N
  • b = 1-a
  • B i (k)是i交换k元素后的概率矩阵。即问题的回答“k交换后i在哪里?”。例如,B 0 (3)= (0 0 1 0 ... 0)并且B 1 (3)= (a 0 b 0 ... 0)。你想要的是每个k的B N (k)。
  • K i 是一个NxN矩阵,在第i列和第i行有1s,在其他地方为零,例如:

kappa_2

  • I i 是单位矩阵,但元素x = y = i归零。例如,对于i = 2:

I_2

  • i

Ai= bIi + aKi

然后,

B_n

但是因为B N (k = 1..N)形成单位矩阵,所以任何给定元素i最后都在位置j的概率由矩阵元素给出(i ,j)矩阵:

solution matrix

例如,对于N = 4:

B_4

作为N = 500的图表(颜色等级为100 *概率):

B_500

所有N&gt; 2的模式相同:

  • 第k个元素最可能的结束位置是k-1
  • k&lt; 最不可能的结束位置为k N * ln(2),位置 1 否则

答案 3 :(得分:13)

我知道我之前见过这个问题......

why does this simple shuffle algorithm produce biased results? what is a simple reason?”在答案中有很多好东西,特别是指向blog by Jeff Atwood on Coding Horror的链接。

正如您可能已经猜到的那样,根据@belisarius的回答,确切的分布在很大程度上取决于要洗牌的元素数量。这是阿特伍德对6元素牌组的情节:

enter image description here

答案 4 :(得分:8)

多么可爱的问题!我希望我有一个完整的答案。

Fisher-Yates很高兴分析,因为一旦它决定了第一个元素,它就会让它孤立无援。有偏见的人可以反复交换元素进出任何地方。

我们可以通过描述作为随机转移矩阵线性地对概率分布起作用的动作,以与马尔可夫链相同的方式对此进行分析。大多数元素都是单独存在的,对角线通常是(n-1)/ n。在通过k时,当它们不被单独留下时,它们与元素k交换(或者如果它们是元素k则与随机元素交换)。这在行k或列k中是1 /(n-1)。行k和列k中的元素也是1 /(n-1)。很容易将这些矩阵相乘,k从1变为n。

我们确实知道最后一个位置的元素同样可能最初位于任何地方,因为最后一个传递与最后一个传递最后一个位置。同样,第一个元素同样可能放在任何地方。这种对称性是因为转置反转了矩阵乘法的顺序。实际上,矩阵在行i与列(n + 1-i)相同的意义上是对称的。除此之外,这些数字并没有显示出明显的模式。这些确切的解决方案确实与belisarius运行的模拟一致:在时隙i中,当j升至i,在i-1处达到其最低值,然后在i处跳到其最高值时,获得j的概率减小,并且减少直到j到达n。

在Mathematica中,我使用

生成了每个步骤
 step[k_, n_] := Normal[SparseArray[{{k, i_} -> 1/n, 
                      {j_, k} -> 1/n, {i_, i_} -> (n - 1)/n} , {n, n}]]

(我没有在任何地方找到它,但使用了第一个匹配规则。) 最终的转换矩阵可以用以下公式计算:

Fold[Dot, IdentityMatrix[n], Table[step[m, n], {m, s}]]

ListDensityPlot是一个有用的可视化工具。

编辑(belisarius)

只是确认。以下代码给出了与@Eelvex答案中相同的矩阵:

step[k_, n_] := Normal[SparseArray[{{k, i_} -> (1/n), 
                      {j_, k} -> (1/n), {i_, i_} -> ((n - 1)/n)}, {n, n}]];
r[n_, s_] := Fold[Dot, IdentityMatrix[n], Table[step[m, n], {m, s}]];
Last@Table[r[4, i], {i, 1, 4}] // MatrixForm

答案 5 :(得分:3)

Wikipedia's page on the Fisher-Yates shuffle有一个描述和示例,说明在这种情况下会发生什么。

答案 6 :(得分:3)

您可以使用stochastic matrices计算分布。令矩阵A(i,j)描述卡最初位于位置j的位置j的概率。然后,如果Ak(i,j) = 1/Ni == k,则kth交换具有由j == k给出的矩阵Ak,(位置k中的卡可以在任何地方结束并且任何卡可以以相等的概率结束在位置k处),Ak(i,i) = (N - 1)/N用于所有i != k(每张其他卡将以概率(N-1)/ N保持在同一位置)并且所有其他元素为零。

然后由矩阵AN ... A1的乘积给出完全洗牌的结果。

我希望你正在寻找概率的代数描述;你可以通过扩展上面的矩阵产品得到一个,但我想它会相当复杂!

更新:我刚刚发现上面的wnoise等效答案!糟糕...

答案 7 :(得分:3)

我进一步研究了这个问题,结果发现这个分布已经详细研究过了。之所以感兴趣是因为这种“破碎”算法在(或曾经)用于RSA芯片系统。

Shuffling by semi-random transpositions,Elchanan Mossel,Yuval Peres和Alistair Sinclair研究了这个以及更普遍的一类洗牌。该论文的结果似乎是,log(n)打破了随机播放以实现接近随机分布。

三个伪随机混淆的偏见 Aequationes Mathematicae ,22,1981,268-292)中,Ethan Bolker和David Robbins分析了这个洗牌并确定总数单次通过后的均匀度变化距离为1,表明它根本不是随机的。他们也进行了渐态分析。

最后,Laurent Saloff-Coste和Jessica Zuniga在他们的非齐次马尔可夫链的研究中发现了一个很好的上界。

答案 8 :(得分:2)

这个问题是要求对所提到的破碎洗牌进行interactive visual matrix diagram分析。这样的工具在Mike Bostock的页面Will It Shuffle? - Why random comparators are bad上。

Bostock汇集了一个分析随机比较器的优秀工具。在该页面的下拉列表中,选择天真交换(随机↦随机)以查看损坏的算法及其生成的模式。

他的页面提供了丰富的信息,因为它可以让人看到逻辑上的改变对改组数据产生的直接影响。例如:

使用非均匀且非常偏向的混洗的矩阵图是使用天真交换(我们从“1到N”中选择)生成的,代码如下:

function shuffle(array) {
    var n = array.length, i = -1, j;
    while (++i < n) {
        j = Math.floor(Math.random() * n);
        t = array[j];
        array[j] = array[i];
        array[i] = t;
    }
}

biased shuffle

但是如果我们实现一个非偏向的shuffle,我们从“k到N”中选择,我们应该看到如下图:

enter image description here

其中分布是统一的,并且由以下代码生成:

function FisherYatesDurstenfeldKnuthshuffle( array ) {
    var pickIndex, arrayPosition = array.length;
    while( --arrayPosition ) {
        pickIndex = Math.floor( Math.random() * ( arrayPosition + 1 ) );
        array[ pickIndex ] = [ array[ arrayPosition ], array[ arrayPosition ] = array[ pickIndex ] ][ 0 ];
    }
}

答案 9 :(得分:1)

到目前为止给出的优秀答案都集中在分发上,但你也问过&#34;如果你犯了这个错误怎么办?&#34; - 这就是我的天赋&#但是,我们已经回答了,所以我将对此作出解释:

Knuth-Fisher-Yates shuffle算法从n个元素中选择1个,然后从n-1个剩余元素中选择1个,依此类推。

你可以使用两个数组a1和a2来实现它,你可以从a1中删除一个元素并将其插入到a2中,但是算法会在适当的位置执行它(这意味着它只需要一个数组),如前所述{{ 3}}(谷歌:&#34;改组算法Fisher-Yates DataGenetics&#34;)非常好。

如果您不删除这些元素,可以再次随机选择它们,这会产生偏向的随机性。这正是您所描述的第二个示例。第一个例子,Knuth-Fisher-Yates算法,使用从k到N的游标变量,它记住已经采用了哪些元素,因此避免多次拾取元素。