在寻找函数的复杂性时如何考虑随机性?

How do you take randomness into account when finding the complexity of a function?

我一直试图理解复杂性,但所有在线 material 让我感到困惑。特别是他们创建实际数学函数的部分。我有一个 for 循环和一个 while 循环。我的困惑来自 while 循环。我知道for循环的复杂度是O(n),但是while循环是基于随机性的。如果选择了一个随机数,并且如果该数字不在列表中,则将其添加并中断 while 循环。但是我的困惑出现在这里,while 循环可能 运行 在最坏的情况下(在我看来) m 次,直到它完成。所以我在想复杂度会是 O(n*m)?

我真的迷路了,需要一些帮助。

大 'O' 符号仅用于最坏的情况。
找出给定循环的最坏情况。
在 'n' 中创建一个函数,并取 'n' 的最高幂并忽略常量(如果有),您将获得时间复杂度。

技术上 worst-case 复杂度为 O(inf):如果我们认为真正的随机生成器 (it isn't, of course) random.randint 可以生成具有相同元素的任意长序列。但是,我们可以估计“average-case”的复杂度。它不是真正的 average-case 复杂度(最佳、最差和平均情况必须由输入定义,而不是随机的),但它可以显示程序将执行多少次迭代,如果我们 运行 它固定n 多次,取平均值。

请注意,列表按此处设置的方式工作(您永远不会添加重复的数字),所以我坚持使用 not in set 比较,而不是 O(1)(而 not in listO(i)) 以消除复杂性来源并稍微简化事情:现在可以使用相同的大 O 限制来估计迭代次数和复杂性。 Single trial 这里是从 [1; n] 上的均匀整数分布中选择。 成功选择的号码不在列表中。

那么在获得不在集合中的项目之前,试验次数的期望值是多少?在代码中的每个步骤之前设置大小是 i。我们可以选择 n-i 个号码中的任何一个。因此成功的概率是 p_i = (n-i)/n(因为分布是均匀的)。每个外部迭代都是几何分布的一个例子:第一次成功之前的试验次数。因此估计 while 次迭代的次数是 n_i = 1 / p_i = n / (n-i)。为了获得最终的复杂性,我们应该对每个 for 迭代的计数求和:sum(n_i for i in range(n))。这显然等于 n * Harmonic(n),其中 Harmonic(n) 是 n-th harmonic number(第一个 n 自然数倒数的总和)。 Harmonic(n) ~ O(log n),因此此代码的“average-case”复杂度为 O(n log n)

对于 list 它将是 sum(i*n / (n-i) for i in range(n)) ~ O(n^2 log(n))(这个等式的证明会稍微长一点)。