我只是在学习时间复杂性,这是我编写的一段代码
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
{
// Spread Peace
}
显然,上面的一个是 O(N ^ 2)的复杂性,它似乎(N == 1e6
)可以永远运行。
这是第二段代码
for (int i = 1; i <= N; i++)
for (int j = i; j <= N; j++)
{
// Money is Everything
}
以上一个也 O(N ^ 2) - N *(N + 1)/ 2 复杂性也在永远运行,但是这段代码:
for (int i = 1; i <= N; i++)
for (int j = i; j <= N; j += i)
{
// Be my GirlFriend
}
只是在一秒钟内执行,我无法得出它的时间复杂度为什么这么快?什么是N == 1e6
的估算?
答案 0 :(得分:4)
让我们首先执行实验,让我们尝试展开循环( C#implementation ),看看发生了什么:
private static IEnumerable<String> Unroll(int N) {
for (int i = 1; i <= N; i++) {
StringBuilder sb = new StringBuilder();
for (int j = i; j <= N; j += i) {
if (sb.Length > 0)
sb.Append(", ");
sb.Append(j);
}
yield return sb.ToString();
}
使用较小数字(例如16
)的测试运行会显示图片
Console.Write(string.Join(Environment.NewLine, Unroll(16)));
你能看到模式,指数下降吗?它看起来像N * log(N)
,对吧?
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
2, 4, 6, 8, 10, 12, 14, 16
3, 6, 9, 12, 15
4, 8, 12, 16
5, 10, 15
6, 12
7, 14
8, 16
9
10
11
12
13
14
15
16
现在是纸和笔的时候了:我们有(对于大N
)
N / 1 items (step == 1) +
N / 2 items (step == 2) +
N / 3 items (step == 3) +
...
N / N items (step == N)
------------------------------
N * (1 + 1/2 + ... + 1/N) =
N * H(N) =
O(N * log(N)) // Harmonic sum H(N) gives log(N)
更准确的估算
H(N) = ln(N) + gamma + 1/(2*N) + ...
,其中
ln() - natural logarithm
gamma - Euler–Mascheroni constant (0.5772156649...)
为您提供N == 1e6
关于14.4e6
循环的信息,实际上有点高估了;实际计数为13970034
(14.0e6
),因为当使用Harmonic系列进行近似时,我们没有采用整数除法(每个k/N
应该整数< / em>,即不考虑k/N
,但floor(k/N)
)。
答案 1 :(得分:0)