有限集中的朴素随机选择的O值是多少?

时间:2009-08-18 13:42:00

标签: math random big-o probability

This question从有限集中获取随机值让我思考......

人们想要从一组Y值中检索X个唯一值是相当普遍的。例如,我可能想从一副牌中交出一手牌。我想要5张卡片,我希望它们都是独一无二的。

现在,我可以天真地做到这一点,通过挑选一张随机卡5次,每次重复时再试一次,直到我得到5张牌。然而,对于大型集合中的大量值,这并不是很好。例如,如果我想从一组1,000,000中获得999,999个值,则此方法会变得非常糟糕。

问题是:有多糟糕?我正在找人解释一个O()值。获得第x个数字将需要y次尝试...但有多少?我知道如何解决任何给定的值,但是有一种直接的方法来推广整个系列并得到一个O()值吗?

(问题不是:“我怎么能改进这个?”因为它相对容易修复,而且我确信它在其他地方已被多次覆盖。)

8 个答案:

答案 0 :(得分:5)

变量

n =集合中的项目总数
m =要从n个项目集中检索的唯一值的数量
d(i) =在步骤i中实现值所需的预期尝试量 i =表示一个特定步骤。 i∈[0,n-1]
T(m,n) =使用朴素算法从一组n个项目中选择m个唯一项目的预期总尝试次数

推理

第一步,i = 0,是微不足道的。无论我们选择哪种价值,我们都会在第一次尝试时获得独一无二的价值。因此:

d(0)= 1

在第二步中,i = 1,我们至少需要1次尝试(我们选择一个有效的唯一值的尝试)。除此之外,我们有可能选择错误的价值。这个机会是(先前挑选的物品的数量)/(物品的总量)。在这种情况下1 / n。如果我们选择了错误的项目,我们可能会再次选择错误的项目。将其乘以1 / n,因为这是我们两次选错的组合概率,给出(1 / n) 2 。要理解这一点,绘制decision tree会很有帮助。选择两次非独特项目后,我们有可能再次这样做。这导致在步骤i = 1中的总预期尝试量中加入(1 / n) 3 。每次我们选错号码,我们都有可能再次选错号码。这导致:

d(1)= 1 + 1 / n +(1 / n) 2 +(1 / n) 3 +(1 / n) 4 + ...

同样,在一般的第i步中,在一个选择中选择错误项目的机会是i / n,导致:

d(i)= 1 + i / n +(i / n) 2 +(i / n) 3 +(i / n) 4 + ... =
= sum((i / n) k ),其中k∈[0,∞]

这是geometric sequence,因此很容易计算它的总和:

d(i)=(1 - i / n) -1

然后通过将每个步骤中的预期尝试量相加来计算总体复杂度:

T(m,n)= sum(d(i)),其中i∈[0,m-1] =
= 1 +(1 - 1 / n) -1 +(1 - 2 / n) -1 +(1 - 3 / n) -1 + ... +(1 - (m-1)/ n)的 -1

将上面系列中的分数扩展为n,我们得到:

T(m,n)= n / n + n /(n-1)+ n /(n-2)+ n /(n-3)+ ... + n /(n-m + 2 )+ n /(n-m + 1)

我们可以使用以下这样的事实:

n /n≤n/(n-1)≤n/(n-2)≤n/(n-3)≤...≤n/(n-m + 2)≤n/(n- M + 1)

由于该系列有m个术语,并且每个术语都满足上述不等式,我们得到:

T(m,n)≤n/(n-m + 1)+ n /(n-m + 1)+ n /(n-m + 1)+ n /(n-m + 1)+ ... + n /(n-m + 1)+ n /(n-m + 1)=
= m * n /(n-m + 1)

通过使用某种技术来评估系列而不是通过(术语数量)*(最大术语)的粗略方法进行限制,可能(并且可能)可以建立稍微更严格的上限

结论

这意味着Big-O指令 O(m * n /(n-m + 1))。我认为没有办法从它的方式简化这个表达式。

回顾结果检查是否有意义,我们看到,如果n是常数,并且m越来越接近n,结果将迅速增加,因为分母得到很小。这是我们所期望的,如果我们例如考虑关于从一组1,000,000中选择“999,999值”的问题中给出的示例。如果我们改为让m是常数并且n实际上非常大,那么复杂性将在极限n→∞中向O(m)收敛。这也是我们所期望的,因为从“接近”无限大小的组中选择恒定数量的项目时,选择先前选择的值的概率基本上为0。由于没有碰撞,我们需要独立于n次尝试。

答案 1 :(得分:4)

如果您已经选择了i值,那么从一组y值中选择一个新值的概率是

(y-i)/y.

因此,获得第(i + 1)个元素的预期试验次数为

y/(y-i).

因此,选择x独特元素的预期试验次数是总和

 y/y + y/(y-1) + ... + y/(y-x+1)

这可以使用harmonic numbers表示为

  

y(H y - H y-x )。

从维基百科页面可以得到近似值

  

H x = ln(x)+ gamma + O(1 / x)

因此,从一组y元素中选择x个唯一元素所需的试验次数 是

y (ln(y) - ln(y-x)) + O(y/(y-x)).

如果需要,可以通过对H x 使用更精确的近似来获得更精确的近似。特别是,当x很小时,可以 很好地改善了结果。

答案 2 :(得分:3)

你的实际问题实际上比我回答的(和更难的)更有趣。我从来没有擅长统计数据(自从我做任何事以来已经有一段时间了),但直观地说,我认为该算法的运行时复杂性可能类似于指数。只要拾取的元素数量与阵列的大小相比足够小,碰撞率就会很小,以至于接近线性时间,但在某些时候碰撞的数量可能会快速增长并且运行 - 时间会流失。

如果你想证明这一点,我认为你必须根据所需数量的元素的预期碰撞次数做一些适度的事情。通过归纳也可能做到,但我认为通过这条路线需要比第一种选择更加聪明。

编辑:在考虑之后,这是我的尝试:

给定一组m元素,并查找n随机和不同的元素。很容易看出,当我们想要选择i元素时,选择我们已经访问过的元素的几率是(i-1)/m。这是该特定选择的预期碰撞次数。对于挑选n元素,预期的碰撞数量将是每个选择的预期碰撞数量的总和。我们将其插入Wolfram Alpha(sum(i-1)/ m,i = 1到n),我们得到答案(n**2 - n)/2m。我们的朴素算法的平均选择数是n + (n**2 - n)/2m

除非我的记忆完全失败(实际上完全可能),否则会给出一个平均情况下的运行时O(n**2)

答案 3 :(得分:3)

如果您愿意假设您的随机数生成器在循环回到先前看到的给定绘图值之前总会找到唯一值,则此算法为O(m ^ 2),其中m为您正在绘制的唯一值的数量。

因此,如果要从一组n个值中绘制m个值,则第一个值将要求您绘制最多1个值以获取唯一值。第二个需要最多2个(你看第一个值,然后是一个唯一值),第三个3,...... mth m。因此,总共需要1 + 2 + 3 + ... + m = [m *(m + 1)] / 2 =(m ^ 2 + m)/ 2次抽取。这是O(m ^ 2)。

如果没有这个假设,我不确定你怎么能保证算法能够完成。这很可能(特别是对于可能有一个循环的伪随机数生成器),您将一遍又一遍地看到相同的值,并且永远不会获得另一个唯一值。

== EDIT ==

对于一般情况:

在你的第一次抽奖中,你将完成1次抽奖。 在你的第二次抽签中,你期望得到1(成功抽奖)+ 1 / n(“局部”抽签代表你画一个重复的机会) 在你的第3次抽奖中,你期望得到1(成功抽奖)+ 2 / n(“部分”抽奖......) ... 在你的第m次抽签中,你期望得到1 +(m-1)/ n平局。

因此,在平均情况下,你将使1 +(1 + 1 / n)+(1 + 2 / n)+ ... +(1 +(m-1)/ n)完全绘制。

这等于[1 + i / n]的i = 0到(m-1)之和。让我们表示总和(1 + i / n,i,0,m-1)。

然后:

sum(1 + i/n, i, 0, m-1) = sum(1, i, 0, m-1) + sum(i/n, i, 0, m-1)
                        = m + sum(i/n, i, 0, m-1)
                        = m + (1/n) * sum(i, i, 0, m-1)
                        = m + (1/n)*[(m-1)*m]/2
                        = (m^2)/(2n) - (m)/(2n) + m 

我们删除低阶项和常数,我们得到这是O(m ^ 2 / n),其中m是要绘制的数字,n是列表的大小。

答案 4 :(得分:2)

有一个漂亮的O(n)算法。它如下。假设您有n个项目,您可以从中选择m个项目。我假设函数rand()产生一个介于0和1之间的随机实数。这是算法:

items_left=n
items_left_to_pick=m
for j=1,...,n
    if rand()<=(items_left_to_pick/items_left)
        Pick item j
        items_left_to_pick=items_left_to_pick-1
    end
    items_left=items_left-1
end

可以证明,该算法确实以相同的概率选取m个项的每个子集,尽管证明不明显。不幸的是,我目前没有参考资料。

编辑这个算法的优点是,与进行随机播放相比,它只需要O(m)内存(假设项目只是整数或可以在运行中生成)需要O(n)内存。

答案 5 :(得分:2)

当您选择全部N个项目时,此算法的最坏情况显然很明显。这相当于要求:平均而言,在每一方至少出现一次之前,我必须掷多少次N面模具?

答案:N * H N ,其中H N 是第N harmonic number

alt text
一个值着名的值log(N)

这意味着相关算法为N log N

作为一个有趣的例子,如果你滚动一个普通的6面模具,直到看到每个数字中的一个,平均需要6个H 6 = 14.7个卷。

答案 6 :(得分:0)

在能够详细回答这个问题之前,让我们定义框架。假设你有一个n个不同对象的集合{a1,a2,...,an},并且想从这个集合中选择m个不同的对象,这样所有对象中给定对象aj出现在概率中的概率是相等的

如果你已经挑选了k个项目,并从全套{a1,a2,...,an}中选择了一个项目,那么之前没有选择该项目的概率是(n-k)/ n。这意味着在获得新对象之前必须采取的样本数量(假设随机采样的独立性)geometric具有参数(n-k)/ n。因此,获得一个额外项目的预期样本数量是n /(n-k),如果k小于n,则接近1。

总之,如果您需要随机选择的m个唯一对象,此算法会为​​您提供

n / n + n /(n-1)+ n /(n-2)+ n /(n-3)+ .... + n /(n-(m-1))

,正如Alderath所示,可以通过

来估算

m * n /(n-m + 1)。

您可以从此公式中看到更多内容:  *随着已选择对象的数量增加(这听起来合乎逻辑),获得新的唯一元素的预期样本数量会增加。  *当m接近n时,你可以期待很长的计算时间,尤其是当n很大时。

为了从集合中获取m个唯一成员,请使用David Knuth's algorithm的变体来获取随机排列。在这里,我假设n个对象存储在一个数组中。

for i = 1..m
  k = randInt(i, n)
  exchange(i, k)
end

这里,randInt从{i,i + 1,... n}采样整数,并且交换翻转数组的两个成员。你只需要洗牌m次,所以计算时间是O(m),而存储器是O(n)(尽管你可以调整它只保存条目,使得a [i]&lt;&gt; i,会给你O(m)的时间和记忆,但有更高的常数)。

答案 7 :(得分:0)

大多数人忘记了抬头,如果号码已经运行,也需要一段时间。

如前所述,nesseary的尝试次数可以从以下方式进行评估:

T(n,m) = n(H(n)-H(n-m)) ⪅ n(ln(n)-ln(n-m))

n*ln(n)

的有趣值,会转到m

但是,对于这些“尝试”中的每一个,您都必须进行查找。这可能是一个简单的O(n) runthrough,或类似二叉树的东西。这将使您的总效果为n^2*ln(n)n*ln(n)^2

对于mm < n/2)的较小值,您可以使用T(n,m) - 不等式对HA进行非常好的近似,得到公式:

2*m*n/(2*n-m+1)

m转到n时,这会给出O(n)次尝试和性能O(n^2)O(n*ln(n))的下限。

然而,所有结果都要好得多,这是我所预期的,这表明在许多非关键的情况下,算法可能实际上很好,你可以偶尔接受更长的运行时间(当你不幸的时候)。 / p>