通常很容易计算最佳情况和最坏情况下的时间复杂度,但是当涉及到平均情况时,尤其是在给出概率p的情况下,我不知道从哪里开始。
让我们看看以下算法来计算矩阵中所有元素的乘积:
int computeProduct(int[][] A, int m, int n) {
int product = 1;
for (int i = 0; i < m; i++ {
for (int j = 0; j < n; j++) {
if (A[i][j] == 0) return 0;
product = product * A[i][j];
}
}
return product;
}
假设p是A[i][j]
为0的概率(即算法在那里终止,返回0);我们如何推导出该算法的平均时间复杂度?
答案 0 :(得分:3)
让我们考虑一个相关的问题。想象一下,你有一枚硬币以概率p翻转头部。多少次,在期待中,你需要在它出现之前翻转硬币吗?答案是1 / p,因为
所以这意味着翻转次数的预期值是
p + 2p(1 - p)+ 3p(1 - p)^ 2 + 4p(1 - p)^ 3 + ...
= p(1(1 - p)^ 0 + 2(1 - p)^ 1 + 3(1 - p)^ 2 + ...)
所以现在我们需要弄清楚这个总结是什么。一般形式是
p从k = 1到无穷大(k(1 - p)^ k)。
不要解决这个特定的总和,而是让它更通用。设x是一个变量,稍后我们将设置为1 - p,但现在我们将其视为自由值。然后我们可以将上述求和重写为
p从k = 1到无穷大(kx ^(k-1))。
现在有一个可爱的技巧:注意这个表达式的内部是x ^ k相对于x的导数。因此,这个总和是
p从k = 1到无穷大(d / dx x ^ k)。
导数是一个线性算子,所以我们可以把它移到前面:
p d / dx和从k = 1到无穷大(x ^ k)
内部和(x + x ^ 2 + x ^ 3 + ...)是1 /(1 - x) - 1的泰勒级数,所以我们可以简化这个以得到
p d / dx(1 /(1 - x) - 1)
= p /(1 - x)^ 2
由于我们选择了x = 1 - p,这简化为
(1 - (1 - p))^ 2
= p / p ^ 2
= 1 / p
呼!这是一个很长的推导。但它表明所需的投币数量为1 / p。
现在,在你的情况下,你的算法可以被认为是投掷mn硬币,它们以概率p出现并且如果它们中的任何一个出现就停止。当然,您需要投掷的预期硬币数量不会超过您允许无限次翻转的情况,因此您的预期运行时间最多为O(1 / p)(假设p> 0) )。
如果我们假设p与m和n无关,那么我们可以注意到,在一些初始增长之后,随着我们增加翻转次数,每次添加的项加入到我们的总和中的指数低于前一次。更具体地说,在大致以对数方式将多个项加入到总和之后,在无限求和的情况下,我们将从总数中删除。因此,假设mn大致大于Θ(log p),则总和最终为Θ(1 / p)。因此,在大O意义上,如果mn独立于p,则运行时为Θ(1 / p)。