关于C#:我只是证明了Eratosthenes筛子的效率比审判部门低吗?

Did I just prove that sieve of Eratosthenes is less efficient than trial division?

我试图比较两种算法的运行速度:一个用于打印质数(10,000个数字)的蛮力C程序和一个Eratosthenes C程序筛网(也包含10,000个质数)。

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

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

但是,有人告诉我,Eratosthenes算法的Sieve比蛮力方法更有效,因此我认为它的运行速度会更快。 所以我错了或者我的程序有缺陷(我对此表示怀疑)。

因此,我的问题是:由于得到的结果与预期相反,这是否证明了Eratosthenes的Sieve在速度方面的确比试验部门效率更低?

我不确定是否相关,但是我使用的是Dev C ++编译器和Windows 7。


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

仅以一个输入大小来衡量两个实现的速度是不够的。通常需要几个数据点来评估我们的代码在运行时的经验增长顺序(因为代码可以在不同的输入大小下运行)。它是基于输入大小比率的运行时间比率的对数。

因此,即使某些输入code_1的运行速度比code_2快10倍,但其运行时间却随着输入大小的每倍增加而翻倍,而对于code_2,它的增长速度仅为1.1x,很快code_2会变为比code_1快得多。

因此,算法效率的真正衡量标准是运行时间复杂度(以及其空间复杂度,即内存需求)。当我们凭经验测量它时,我们仅测量是否针对手头的特定代码(在特定的输入大小范围内)而不是算法本身,即它的理想实现。

特别是,在产生的n个素数中,试验划分的理论复杂度为O(n^1.5 / (log n)^0.5),通常被视为~ n^1.40..1.45经验增长顺序(但对于较小的输入量,最初可能为~n^1.3)。对于Eratosthenes筛子,它是O(n log n log (log n)),通常被视为~ n^1.1..1.2。但是,肯定有在~n^2.0或更糟糕的情况下进行试验的部门和Eratosthenes筛子的次佳实现。

所以不,这没有证明。一个数据点是没有意义的,至少需要三个数据点才能获得"全局",即能够确定地预测较大输入大小所需的运行时间空间。

科学方法的目的在于确定性地进行预测。

顺便说一句,您的运行时间很长。 10,000个素数的计算应该几乎是瞬时的,对于在快速框上运行的C程序而言,它的秒数要不到1/100秒。也许您也在测量打印时间。别。 :)


不,经过的运行时间并不是衡量效率的标准,因为运行时间因平台而异-说"我的算法在10秒钟内运行"几乎没有提供关于算法本身的信息。除此之外,您还需要列出整个环境规范和同时运行的其他进程,这会造成很大的麻烦。因此,订单符号(大哦,小哦,欧米茄等)的发展。

效率通常分为两个部分:

  • 时间效率。
  • 空间效率。
  • ...其中一种算法可能具有极高的时间效率,但在空间方面却效率极低。反之亦然。当缩放需要为给定输入n执行的指令量时,将基于算法的渐近行为来分析算法。这是博士学位计算机科学家精心研究的一个领域的高级解释-我建议您在这里阅读更多有关它的信息,以获取最佳的低级解释。

    注意,我附加了Big Oh符号的链接-姐妹符号都可以在该Wikipedia页面上找到,通常是一个不错的起点。它还将涉及时空效率的差异。

    使用Big Oh的时间效率小应用程序:

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

    1
    2
    3
    4
    5
    6
    (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)。我们还注意到,在最坏的情况下,在input_arest的第三个条件和第四个条件下调用fn_a。然后,我们可以将递归关系写为T(n)= O(1)+ 2T(n-1)。在递归关系图上查找,我们发现fn_a的阶为O(2 ^ n),因为在最坏的情况下,会进行两次递归调用。

    还需要注意的是,根据Big Oh的正式定义,声明fn_a为O(3 ^ n)也是正确的(但无用)。分析时使用Big Oh表示了很多算法,但是使用Big Theta来收紧界限更合适,本质上是指:相对于给定算法而言,最低,最准确的顺序。

    注意,请阅读正式定义!


    Does a longer run-time mean a less efficient algorithm?

    不必要。程序的效率不仅通过其花费的时间来衡量,而且还通过它所占用的资源来衡量。在考虑效率时,空间是另一个要牢记的因素。

    从维基:

    For maximum efficiency we wish to minimize resource usage. However,
    the various resources (e.g. time, space) can not be compared directly,
    so which of two algorithms is considered to be more efficient often
    depends on which measure of efficiency is being considered as the most
    important, e.g. is the requirement for high speed, or for minimum
    memory usage, or for some other measure?


    通常通过算法处理大型输入的效率来衡量算法的效率。 10,000个数字不是很大的输入,因此您可能需要使用更大的数字才能使Eratosthenes的筛子开始变得更快。

    另外,您的一种实现可能很大

    最后,可以通过所需的内存量来衡量算法的效率(但是这种方法并不常见,特别是因为当今的内存是如此便宜)


    简而言之,是的,如果您通过效率来表示节省时间。也有内存注意事项。

    不过要小心如何测量-确保计时工具准确无误。

    确保没有其他任何运行时在同一台计算机上进行测量。

    确保测量几次,并取平均值和平均值进行比较。

    考虑让某人查看您的代码以检查它是否在执行您认为正在执行的操作。


    总的来说:是的,但是当您处于1秒以下的范围内时,会产生很多噪音,这可能会令人困惑...

    多次运行每个测试,并对结果使用一些统计信息(例如,平均值或均值/偏差取决于您的关注程度)

    和/或使其做更多的工作-例如查找更多的质数