对于 6-from-50,我不太确定是否会担心效率,因为重复的可能性相对较低(根据我粗略的计算,总体而言为 30%)。你可以很容易地记住你之前生成的数字并将它们扔掉,比如(伪代码):
n[0] = rnd(50)
for each i in 1..5:
n[i] = n[0]
while n[1] == n[0]:
n[1] = rnd(50)
while n[2] == any of (n[0], n[1]):
n[2] = rnd(50)
while n[3] == any of (n[0], n[1], n[2]):
n[3] = rnd(50)
while n[4] == any of (n[0], n[1], n[2], n[3]):
n[4] = rnd(50)
while n[5] == any of (n[0], n[1], n[2], n[3], n[4]):
n[5] = rnd(50)
但是,当您从 6-from-50 移动到 48-from-50 或 6-from-6 时,这种情况会被打破,因为重复的可能性开始变得远。那是因为可用数字的池越来越小,而你最终扔掉的数字越来越多。
对于一个非常有效的解决方案,它可以为您提供一个零重复可能性的值子集(并且没有不必要的预先排序),Fisher-Yates 是要走的路。
dim n[50] // gives n[0] through n[9]
for each i in 0..49:
n[i] = i // initialise them to their indexes
nsize = 50 // starting pool size
do 6 times:
i = rnd(nsize) // give a number between 0 and nsize-1
print n[i]
nsize = nsize - 1 // these two lines effectively remove the used number
n[i] = n[nsize]
只需从池中选择一个随机数,将其替换为该池中的顶部数字,然后减小池的大小,您就可以进行洗牌,而不必担心预先进行大量交换。
如果数字很大,这一点很重要,因为它不会引入不必要的启动延迟。
例如,检查以下基准检查,选择 10-from-10:
<------ n[] ------>
0 1 2 3 4 5 6 7 8 9 nsize rnd(nsize) output
------------------- ----- ---------- ------
0 1 2 3 4 5 6 7 8 9 10 4 4
0 1 2 3 9 5 6 7 8 9 7 7
0 1 2 3 9 5 6 8 8 2 2
0 1 8 3 9 5 6 7 6 6
0 1 8 3 9 5 6 0 0
5 1 8 3 9 5 2 8
5 1 9 3 4 1 1
5 3 9 3 0 5
9 3 2 1 3
9 1 0 9
您可以看到池在减少,因为您总是用未使用的替换使用过的,因此您永远不会重复。
使用返回的结果作为集合中的索引将保证不会选择重复的项目。