我是否只是证明了Eratosthenes的筛子效率低于试验师?

时间:2013-08-16 09:25:37

标签: c algorithm primes performance sieve-of-eratosthenes

我试图比较两种算法的运行时速度:一个强力C程序打印素数(10,000个数字),一个Sieve of Eratosthenes C程序(也是10,000个素数)。

我测得的筛选算法的运行时间为:0.744秒

我测得的蛮力算法的运行时间是:0.262秒

然而,有人告诉我,Eratosthenes Sieve算法比蛮力方法更有效,所以我认为它会更快运行。所以要么我错了,要么我的程序有缺陷(我怀疑)。

因此,我的问题是:由于我得到了与我预期相反的结果,这是否证明了Eratosthenes的Sieve在速度方面确实是 less 高效算法,到试验区?

我不确定它是否有任何意义,但我使用的是Dev C ++编译器和Windows 7。

6 个答案:

答案 0 :(得分:7)

TL; DR: 比较一个输入大小的代码变体的速度是没有意义的;比较经验增长的顺序真实地反映了代码的算法性质,并且对于相同的输入大小测试范围,将在不同的测试平台上保持一致。比较绝对速度值仅对具有相同渐近或至少局部生长行为的代码变体有意义。


仅在一个输入尺寸下测量两个实现的速度是不够的。通常需要几个数据点来评估代码的运行时间empirical orders of growth(因为代码可以使用不同的输入大小运行)。它以运行时间比率的对数为基础,以输入尺寸比率为基础。

因此,即使某些输入code_1code_2运行 10 倍,但每次加倍输入时其运行时间加倍大小,而对于code_2,它只会增长为 1.1x ,很快code_2将比code_1快得多。

因此,算法效率的真正衡量标准是run time complexity(以及其空间的复杂性,即内存要求)。当我们根据经验测量它时,我们只测量手头的特定代码(在特定输入大小范围内),而不是算法本身,即它的理想实现。

特别是,试验分裂的理论复杂性是O(n^1.5 / (log n)^0.5),在 n 素数中产生,通常被视为~ n^1.40..1.45经验增长的顺序(但它可以是{{} 1}}最初,对于较小的输入尺寸)。对于Eratosthenes的筛子,它是~n^1.3,通常被视为O(n log n log (log n))。但是,试验师和Eratosthenes的筛子肯定存在次优实施,其运行时间为~ n^1.1..1.2甚至更差。

所以 没有 ,这证明什么都没有。一个数据点是没有意义的,至少需要三个来获得“全局”,即能够预测,确定更大输入大小所需的运行时间/空间。

具有已知确定性的

预测scientific method的全部内容。


BTW你的运行时间很长。 10,000个素数的计算应该几乎是瞬时的,远远低于在快速盒子上运行的C程序的1/100秒。也许你也在测量打印时间。别。 :)

答案 1 :(得分:6)

不,经过的运行时间不是测量效率的标准,因为它在不同平台之间变化 - 说“我的算法在10秒内运行”几乎没有提供有关算法本身的信息。除此之外,您还需要列出整个环境规范和同时运行的其他进程,这将是一个巨大的混乱。因此,订单符号的发展(Big Oh,Little Oh,Omega等)。

效率通常分为两个小节:

  1. 时间效率。
  2. 空间效率。
  3. ......其中一种算法可能具有极高的时间效率,但在空间方面效率非常低。反之亦然。在扩展给定输入n时需要执行的指令量时,算法会根据它们的渐近行为进行分析。这是对博士计算机科学家精心研究的领域的一个非常高级的解释 - 我建议你阅读更多关于here的内容,以获得最佳的低级解释。

    注意,我附上了Big Oh表示法的链接 - 姐妹符号都可以在Wikipedia页面上找到,它通常是一个很好的起点。它也会影响空间和时间效率的差异。

    使用Big Oh实现时间效率的小应用:

    考虑一下Racket中的以下递归函数(如果我知道它将在Python中 - 我可以做的最好的伪代码):

    (define (fn_a input_a)
      (cond
        [(empty? input_a) empty]
        [(empty? (rest input_a)) input_a]
        [(> (first input_a) (fn_a (rest input_a))) (cons (first input_a) empty)]
        [else (fn_a (rest input_a))]))
    

    ...我们看到:empty?rest>first都是O(1)。我们还注意到,在最糟糕的情况下,在第三个条件下调用fn_a,在rest input_a调用第四个条件。然后我们可以将递归关系写为T(n)= O(1)+ 2T(n - 1)。在递归关系图表中查看,我们看到fn_a的顺序为O(2 ^ n),因为在最坏的情况下,会进行两次递归调用。

    同样重要的是要注意,通过Big Oh的正式定义,表明fn_a是O(3 ^ n)也是正确的(无论如何)。分析时使用Big Oh表示很多算法,但是使用Big Theta来收紧边界更合适,本质上意味着:相对于给定算法的最低,最准确的顺序。

    小心,阅读正式定义!

答案 2 :(得分:2)

  

较长的运行时间是否意味着效率较低的算法?

没必要。程序的效率不仅取决于它所花费的时间,而且取决于它所采用的资源。在考虑效率的同时,空间是另一个要考虑的因素。

来自wiki: -

  

为了最大限度地提高效率,我们希望尽量减少资源使然而,   各种资源(例如时间,空间)无法直接比较,   因此,两种算法中的哪一种被认为通常更有效   取决于哪种衡量效率被视为最多   重要的,例如是高速或最低要求   内存使用情况,还是其他措施?

答案 3 :(得分:1)

一般情况下:是的,但是当你在低于1秒的范围内时,有很多噪音可能令人困惑......

多次运行每次测试并在结果上使用一些统计数据(例如平均值或平均值/偏差取决于您关心的程度)

和/或让它做更多的工作 - 比如找到更多的素数

答案 4 :(得分:1)

简而言之,是的,如果效率,则意味着时间效率。还有内存考虑因素。

要小心你的测量方法 - 确保你的计时工具是精确的。

确保在没有其他运行时在同一台机器上进行测量。
确保你多次测量并取平均值和变量进行相当的比较。
考虑让某人检查您的代码,以检查它是否正在执行您认为正在执行的操作。

答案 5 :(得分:1)

算法的效率通常通过它们处理大量输入的效率来衡量。 10,000个数字不是一个非常大的输入,所以你可能需要在Eratosthenes的筛子开始变得更快之前使用更大的数字。

或者,您的某个实施可能会有很大的优势

最后,算法的效率可以通过所需的内存量来衡量(但这种方法不太常见,特别是因为现在内存非常便宜)