假设我们要编写一种方法来制作一张洗牌的纸牌。现在让它变得非常简单,无视套装,所以我们有52张牌。
一种算法是:
另一种算法:
我知道有更好的改组算法,但关于这两个,哪一个更好,为什么?
答案 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 mentioned是Fisher-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)
更好的是:
在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);