我想了解Big-O表示法。很抱歉,如果我问的东西太明显了,但我似乎无法解决这个问题。
我有以下C#代码函数,我正在尝试计算Big-O表示法。
for (i = 2; i < 100; i++)
{
for (j = 2; j <= (i / j); j++)
if ((i % j) == 0) break; // if factor found, not prime
if (j > (i / j))
Console.WriteLine("{0} is prime", i);
}
现在我得到的是,我认为if
子句被认为是常数O(1)并且在计算此算法时没有考虑到这一点?如果我正确理解了一个for循环
for(i = 0; i < 100; i++)
因为它是一个线性函数是O(n)和一个不依赖于周围循环变量的嵌套循环
for(i = 0; i < 100; i++)
for(j = 0; j < 100; j++)
是O(n ^ 2)?但是我如何计算一个函数,例如第二个循环依赖于第一个循环并创建非线性函数的顶部函数?
我找到了一个名为
的linearithmic定义线性算法可以扩展到巨大的问题。每当N加倍时, 比双打更多(但不多)的运行时间。
虽然这似乎是对这段代码片段如何运行的一个很好的描述,这意味着它是O(N Log [n]),如果是这样,我怎么能计算出来?
答案 0 :(得分:6)
@Jon很接近,但他的分析有点不对,算法的真正复杂度为O(n*sqrt(n))
。
这是基于以下事实:对于每个数字i
,您应该在内循环中执行的预期“工作”数量为:
1/2 + 2/3 + 3/4 + ... + (sqrt(i)-1)/sqrt(i) =
= 1-1/2 + 1-1/3 + ... + 1-1/sqrt(i)
= sqrt(i) - (1/2 + 1/3 + ... + 1/sqrt(i)
= sqrt(i) - H_sqrt(i)
由于H_sqrt(i)
(The harmonic number)位于O(log(sqrt(i)) = O(1/2*log(i)
,我们可以得出结论,每个素数计算的复杂度为O(sqrt(i)-log(i)) = O(sqrt(i))
。
由于每个i
重复执行此操作,因此问题的总复杂性为O(sqrt(2) + sqrt(3) + ... + sqrt(n))
。根据{{3}},平方根的总和在O(n*sqrt(n))
,比O(nlogn)
“更差”。
需要注意的事项:
j > (i / j)
的点。(j-1)/j
的第一笔总和为j
,因为平均有j
个元素中的一个进入休息时间(1/3的元素可以分为3,1 / 4 by 4,...)这使我们(j-1)/j
不是 - 这是我们所期望的工作。O(log(sqrt(n)) = O(1/2*log(n)
,等级O(log(n^k))=O(k*log(n))=O(log(n))
来自k
。 (在你的情况下,k = 1/2)答案 1 :(得分:1)
分析你的算法,我想出了以下内容:
i
位于[2, 3]
区间时,内部循环不会迭代。i
位于[4, 8]
区间时,内部循环会迭代一次。i
位于[9, 15]
区间时,内部循环会重复两次。i
位于[16, 24]
区间时,内部循环会重复三次。i
位于[25, 35]
区间时,内部循环会重复四次。i
位于[36, 48]
区间时,内部循环会重复五次。i
位于[49, 63]
区间时,内部循环会重复六次。i
位于[64, 80]
区间时,内部循环会重复七次。i
位于[81, 99]
区间时,内部循环会重复八次。
我不得不去超过100的范围来验证上述情况。i
位于[100, 120]
区间时,内部循环会重复九次。取决于i's
值的间隔可以表示如下:
[i^2, i * (i + 2)]
因此,我们可以这样做:
经验验证:
使用有用的WolframAlpha链接:
http://www.wolframalpha.com/input/?i=sum[+floor%28+i^%281%2F2%29%29+-+1+]+with+i+from+2+to+99.
在形式上,我们可以说明以下内容:
答案 2 :(得分:0)
我看了你的代码 - 而且没有n。代码不依赖于任何n。它将始终以完全相同的固定时间运行。你可以计算它需要多长时间,但它始终是相同的,恒定的,时间。如上所述,以“for(i = 2; i&lt; 100; ++ i)”开头的代码在O(1)中运行。
所以将第一行改为
for (i = 2; i < n; ++i)
现在我们的代码实际上取决于n。内循环最多运行sqrt(i)次迭代,小于sqrt(n)。外循环运行大约n次迭代。因此执行时间最多为O(n sqrt(n))= O(n ^ 1.5)。
实际上,它会跑得更快。它通常不会达到sqrt(i),但只有在找到i的除数之前。一半数字可被2整除(一次迭代)。其余三分之一可被3整除(两次迭代)。其余五分之一可被5整除(四次迭代)。关于n / nn个数是具有sqrt(n)次迭代的素数。关于n / ln n个数是两个素数的乘积&gt; n ^(1/3),最多迭代sqrt(n)次。其余的迭代次数少于n ^(1/3)。
因此代码实际上以O(n ^ 1.5 / ln n)运行。您可以通过使用一个最大为sqrt(n)的素数表来改进这一点,并且您将降低到O(n ^ 1.5 / ln ^ 2 n)。
但实际上,你可以打赌,Console.WriteLine()比检查一个数字是素数要花费更长的时间。如果你坚持列出所有素数,你的算法将由O(n / ln n)时间控制,并且显示结果的非常大的常数因子直到n变得非常大。