改组算法之间的区别

时间:2013-05-31 18:56:46

标签: algorithm shuffle array-algorithms

假设我们要编写一种方法来制作一张洗牌的纸牌。现在让它变得非常简单,无视套装,所以我们有52张牌。

一种算法是:

  • 填充52个元素的数组,第一个元素为1,第二个元素为2,依此类推。
  • 编写一个迭代X次的for循环,并在每次迭代中挑选两张随机卡并交换它们。
  • X值越大,随机播放的随机性就越大。

另一种算法:

  • 像以前一样填充数组。
  • 编写一个迭代26次的for循环,并在每次迭代中选取两个随机数,并将这两个数字放在另一个存储新选数字的52元素数组的前面。
  • 在每次迭代中,从原始数组中删除添加到新数组的两张卡。

我知道有更好的改组算法,但关于这两个,哪一个更好,为什么?

4 个答案:

答案 0 :(得分:1)

你必须用“更好”来定义你的意思。第一种算法的问题在于某些元素可能永远不会改变位置。例如,如果您从未随机获得较低的数字,则第一张卡片将按顺序排列。

第二种算法可以让你获得更多随机化。但是,如果你只运行一次,则可以在最终位置预测项目。

我要么多次运行算法2,要么就像你做一个真正的套牌一样洗牌?

1: Split the deck into two arrays of 26
2: Take the top card from one of the arrays at random and put it into a new array of size 52
3: Keep doing this until one array is empty, put the remaining cards of the other array into the size 52 array
4: Repeat

这将为您提供一个很好的随机化

答案 1 :(得分:1)

第二种算法似乎是Fisher-Yates的展开式实现。这种混洗具有选择具有均匀分布的所有可能结果之一的特性。大多数人称之为“公平”或“完美”的洗牌。如果随机数生成器提供无偏的结果,则无需重复操作以获得额外的随机性。

第一种算法可能会渐近地接近我不知道什么样的分布。我会出于各种原因避免它。主要是因为我不知道在它产生良好的洗牌之前需要多大的X,但我确定它超过了52.除了故意模拟不适当的洗牌外,我想不出这个算法的好应用。

第一个算法正在运行,这在某些情况下是有益的,但是如果你想要那么你可以修改第二个算法以类似的方式运行。

答案 2 :(得分:1)

第一种算法会产生高度偏向的分布,因为它可能会使某些卡位于其初始位置并且容易受到“双重交换”问题的影响(将两张卡交换两次会导致初始卡状态)。

第二个算法as @sh1 mentionedFisher-Yates algorithm的展开版本,只有一个例外:它不再是线性的,因为从列表中删除项本身就是线性操作,并且在每次迭代时执行,因此整体算法复杂度为O(n ^ 2)。

Fisher-Yates algorithm的有效版本如下:

在伪代码中(因为你没有提到选择的语言)

for i from n − 1 downto 1 do
   j ← random integer with 0 ≤ j ≤ i
   exchange a[j] and a[i]

和python实现,以防万一:)

import random
n = 52
arr = [i for i in range(1,n+1)]

for i in range(n-1, 1, -1):
    elem_to_swap = random.randint(0, i)
    arr[elem_to_swap], arr[i] = arr[i], arr[elem_to_swap]

答案 3 :(得分:0)

更好的是:

  1. 在某个临时数组中生成52个数字
  2. 使用临时数组作为键
  3. 为卡片命令

    在C#看起来

    Random r = new Random(DateTime.Now.Miliseconds);
    string [] cards = new string[52]{"1","2",.....};
    int [] temp = new int[52];
    for(int i=0;i<52;i++)
    {
        temp[i]=r.Next();
    }
    
    Array.Sort(temp,cards);