给定两个数字m和n,m小于n,返回一组从1到n的m个随机唯一数。例如,如果您有m = 6且n = 49,这与给出随机抽奖号码相同。
这样做的一种方法是使用随机数生成器获取0到1之间的数字,乘以n,截断为整数,检查数字是否已经设置,如果没有添加到set,重复直到我们有m个数字
另一种方法是首先创建一个数字为1到n的数组,随机重新排序数字,读取前m个数字。
我认为如果m与n相比较小,则第一个更好,例如,如果m = 1且n = 1000000,则第一个显然要快得多。但是如果m很大,我认为第二种方法会更好,例如,如果m = 500000和n = 1000000,如果你继续获得已经添加到的数字,那么第一种方法可能需要重复很多次。集。
我的问题是,是否有一种数学方法可以计算m和n之间的关系,以便何时使用一种方法比另一种方法更有效?
答案 0 :(得分:1)
如果你从数组[1,2,...,n]开始,你可以重复地将第i个元素与从i到n随机选择的位置中的元素交换。为1< = i< = m执行此操作,您就完成了。
如果m<<< ñ。通过制作从整数到整数的映射,可以在不使用数组的情况下复制此行为。按上述步骤操作,但不是从数组的第i个位置读取,而是检查我是否是地图的键,然后拉出存储的值。类似地,您将元素放入第i个位置,而是将其作为与键i关联的值存储在地图中。
这是O(m)。
答案 1 :(得分:1)
我们通过它time complexity来衡量算法,它表示时间成本随着输入大小的增加而增加的速度。
在您的情况下,您的第一个算法无法正常工作。生成一个随机数,直到它不等于给定数字,有机会花费无限时间。所以让我们通过以下方式改进它:
这种改进的算法具有时间复杂度的上限。无论使用线性列表还是链表,步骤3~4的复杂度为O(M)。他们将重复M次。此算法的总体复杂性为 O(NM)
。
使用Fisher–Yates shuffle,第二种算法的复杂性为 O(N)
。
所以,第二种算法是复杂性的赢家。请注意,这只意味着当输入大小增加时,获胜者的时间成本会增加 。它并不意味着总是比另一个花费更少的时间。 我们不会根据其绝对时间成本衡量算法,因为它因硬件,系统,语言和编译器等不同而不同。我们需要时间复杂度。 < / p>
答案 2 :(得分:1)
扩展我的评论。在我们选择一个尚未在集合中的元素之前,让E [X_i]成为预期的绘制数量。如果选择不在集合中的内容的概率是p
,那么我们有
E[X_i] = 1/p
要看到这一点,请认为我们选择正确(概率为p),或者我们没有在这种情况下添加平局,因此
E[X_i] = p + (1 - p) * (1 + E[X_i])
E[X_i] = 1/p
。
现在对于第i个元素(从0开始),p = (n - i) / n
,因此E[X_i] = n / (n - i)
。使用E[X_i]
对所有i < m
进行求和,得到预期的抽取次数,以提取m
个数字。对于m <= n/2
,这明显优于创建n
元素列表并对其进行混洗。