费希尔 - 耶茨洗牌
所述费-耶茨洗牌是一种算法,用于产生随机排列的有限的序列 -in平原而言,算法打乱的序列。该算法有效地将所有元素放在帽子里; 它通过随机从帽子中绘制一个元素直到没有元素保留下来,从而不断确定下一个元素。该算法产生无偏置排列:每个排列的可能性相同。该算法的现代版本是有效的:它需要的时间与被洗牌的项目数和打乱他们的地方。
费雪耶茨洗牌得名罗纳德·费舍尔和弗兰克·耶茨,谁第一个描述它,并且也被称为克努特洗牌后,高德纳。被称为Sattolo算法的Fisher-Yates混洗的变体可用于生成长度为n的随机循环置换,而不是随机置换。
费希尔和耶茨的原始方法
Fisher-Yates shuffle以其原始形式在1938年由Ronald Fisher和Frank Yates在他们的生物,农业和医学研究统计表中描述。他们对算法的描述使用铅笔和纸张; 随机数字表提供了随机性。用于生成数字1到N的随机置换的基本方法如下:
- 写下从1到N的数字。
- 在一个和剩余未使用数字(包含)之间选择一个随机数k。
- 从低端算起,删除尚未删除的第k个数字,并将其写在单独列表的最后。
- 从第2步开始重复,直到所有数字都被删除。
- 在步骤3中记下的数字序列现在是原始数字的随机排列。
假设在上面的步骤2中选取的随机数是真正的随机和无偏的,那么所得到的置换也是如此。费舍尔和耶茨仔细描述了如何从所提供的表格中以任何希望的范围获得这样的随机数字,避免任何偏见。他们还建议使用一种更简单的方法 - 从一个到N选取随机数字并丢弃任何重复数据 - 以生成前半部分置换,并且仅将更复杂的算法应用于剩下的一半,在那里选择重复的数字否则会变得令人沮丧的常见。
现代算法
费雪耶茨洗牌的现代版,专为电脑使用,通过引进理查德Durstenfeld于1964年,并通过推广唐纳德·E·克努特在计算机程序设计艺术为“算法P(洗牌)”。Durstenfeld和Knuth在他的书的第一版中都没有承认费雪和耶茨的工作; 他们可能没有意识到这一点。随后的计算机编程艺术版提到了费雪和耶茨的贡献。[4]
Durstenfeld描述的算法不同于Fisher和Yates给出的算法,只是一个小而重要的方法。然而,费舍尔和耶茨方法的一个天真的计算机实现将花费不必要的时间来计算上述步骤3中的剩余数字,而Durstenfeld的解决方案是通过将每个“未击中”数字交换到列表末尾,迭代。与O(n 2)相比,这样可以将算法的时间复杂度降低到O(n)。此更改提供以下算法(对于基于零的数组)。
-要随机阵列一个的Ñ:元素(索引为0..n-1)
为 我 从 Ñ -1 DOWNTO 1 做
Ĵ ←随机整数,使得0≤ Ĵ ≤ 我
交换一个 [ Ĵ ]和一个 [ 我 ]
将数组以相反方向(从最低索引到最高索引)进行混洗的等效版本是:
-要随机阵列一个的Ñ元素(索引为0..n-1):
为 我 从 0 到 Ñ -2 做
Ĵ ←随机整数使得i&le; Ĵ < Ñ
交换一个 [ 我 ]和一个 [j]的
示例
铅笔和纸张法
作为例子,我们将使用Fisher和Yates的原始方法将数字从1到8进行排列。我们将从一张草稿纸上写出数字开始:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1 2 3 4 5 6 7 8 |
现在我们从1到8 滚动一个随机数k - 让我们把它做成3 - 然后敲出便笺本上的第k个(即第3个)数字(当然是3),并将结果写下来:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1-8 | 3 | 1 2 3 4 5 6 7 8 | 3 |
现在我们选择第二个随机数,这次是从1到7:结果是4.现在我们删除第四个数字,这个数字尚未从便笺本中删除 - 这是数字5 - 并将其添加到结果中:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1-7 | 4 | 1 2 3 4 5 6 7 8 | 3 5 |
现在我们从1到6然后从1到5选择下一个随机数,依此类推,总是重复上面的删除过程:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1-6 | 五 | 1 2 3 4 5 6 7 8 | 3 5 7 |
1-5 | 3 | 1 2 3 4 5 6 7 8 | 3 5 7 4 |
1-4 | 4 | 1 2 3 4 5 6 7 8 | 3 5 7 4 8 |
1-3 | 1 | 1 2 3 4 5 6 7 8 | 3 5 7 4 8 1 |
1-2 | 2 | 1 2 3 4 5 6 7 8 | 3 5 7 4 8 1 6 |
1 2 3 4 5 6 7 8 | 3 5 7 4 8 1 6 2 |
现代方法
我们现在使用Durstenfeld的算法来做同样的事情:这次,我们将它们替换为尚未选择的最后一个数字,而不是将所选数字删除并复制到别处。我们将从以前的1到8中写出数字开始:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1 2 3 4 5 6 7 8 |
对于我们的第一卷,我们从1到8滚动一个随机数:这次是6,所以我们将列表中的第6和第8个数字交换:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1-8 | 6 | 1 2 3 4 5 8 7 | 6 |
下一个随机数从1滚到7,结果是2.因此,我们交换第2和第7个数字并继续前进:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1-7 | 2 | 1 7 3 4 5 8 | 2 6 |
我们推出的下一个随机数是1到6,恰好是6,这意味着我们将第6个数字留在列表中(在上面的交换之后,现在是数字8),然后移动到下一个数字步。再次,我们以相同的方式进行,直到排列完成:
范围 | 滚 | 刮 | 结果 |
---|---|---|---|
1-6 | 6 | 1 7 3 4 5 | 8 2 6 |
1-5 | 1 | 5 7 3 4 | 1 8 2 6 |
1-4 | 3 | 5 7 4 | 3 1 8 2 6 |
1-3 | 3 | 5 7 | 4 3 1 8 2 6 |
1-2 | 1 | 7 | 5 4 3 1 8 2 6 |
在这一点上,没有什么可以做的了,所以产生的排列是7 5 4 3 1 8 2 6。