当前位置: 首页 > 知识库问答 >
问题:

这比厄拉多塞的筛子更有效吗?

凤安然
2023-03-14

我用Python2.7编写了一段代码,用于创建质数列表。代码

def primes_list(num):
    ans = [2]
    for i in range(3, num, 2):
        for j in ans:
            if i % j == 0:
                break
        else:
            ans.append(i)
    else:
        return ans

这是否比埃拉托斯特尼筛更有效?我认为记忆效率应该更好,但我怀疑时间效率。如何计算时间和内存效率,以及如何对效率进行基准测试?

共有3个答案

薛涛
2023-03-14

测量增长的经验顺序,就是这样!:)

例如,对于来自已接受答案的数据,log_10(50.41/0.9) = 1.75log_10(2.31/0.21) = 1.04,因此您的TD(试验除法)代码(在1,000范围内)的生长顺序约为n^1.75经验增长顺序。10,000) vs. ~ n^1.04 埃拉托斯特尼筛子。

后者符合< code>n log log n,而前者符合n^2 / log(n)^2,这是理所应当的。

用< code>g(n) = n^2 / log(n)^2,我们得到< code > g(10000)/g(1000)= 56.25 ,这与经验值< code>50.41/0.9 = 56.01仅相差0.4%。但是对于< code>g2(n) = n^2 / log(n),我们得到< code > G2(10000)/G2(1000)= 75 ,这与证据相差甚远。

关于时间复杂性:事实上,大多数复合材料都很早就失效了(是小素数的倍数)。要生成k=n/log(n)素数,这里需要O(k^2)时间,通过所有前面的素数测试每个素数,仅测试那些不超过其平方根的素数。

复合物不会增加复杂性(M. ONeill的JFP文章(第4页)中的精确分析给出了与测试sqrt时素数相同的复合物复杂性 - 每个复合材料的素因数保证不大于其sqrt - 因此复合材料的复杂性甚至低于此处素数的复杂性)。

所以总的来说,它是O(k^2),也就是说,O(n^2/对数(n)^2)

只需添加两行代码,就可以大大提高代码的速度,从O(n^2/log(n)^2)O(n*1.5/log(n

    for j in ans: 
        if j*j > i: 
            ans.append(i); break; 
        if i % j == 0:
            break
    # else:
    #     ans.append(i)

时间复杂性的提高意味着10000与1000的运行时间比17.8x,而不是像以前那样56.25x。这意味着在此范围内的经验增长顺序为~n^1.25(而不是~n^1.75)。10000次调用的绝对运行时间将比原来的50.41秒更接近2.31秒。

顺便说一句,你的原始代码相当于大卫特纳的著名代码,

primes = sieve [2..]
sieve (x:xs) = x : sieve [y | y <- xs, rem y x /= 0]

和改进的代码,如下所示:

primes = 2 : sieve [3..] primes 
sieve xs (p:ps) | (h,t) <- span (< p*p) xs =
              h ++ sieve [y | y <- t, rem y p /= 0] ps 

(代码在哈斯克尔,我认为它具有足够的可读性。x:xs 代表一个列表,其中 x 是 head 元素,x 是列表的其余部分)。

翟单弓
2023-03-14

不,这是试验除法,在时间复杂度上比eratosthenes的筛子差得多。它的空间复杂度稍微好一点,但是,由于素数大约是n/log(n),所以你没有节省大量的内存。此外,筛子可以使用位向量来完成,它可以将常数减少32/64倍(因此出于实际目的,它可能会更好)。

显示计时差异的小型基准测试:

>>> timeit.timeit('primes_list(1000)', 'from __main__ import primes_list', number=1000)
0.901777982711792
>>> timeit.timeit('erat(1000)', 'from __main__ import erat', number=1000)
0.2097640037536621

如您所见,即使使用n=1000eratothenes,其速度也要快4倍以上。如果我们将搜索增加到10000

>>> timeit.timeit('primes_list(10000)', 'from __main__ import primes_list', number=1000)
50.41101098060608
>>> timeit.timeit('erat(10000)', 'from __main__ import erat', number=1000)
2.3083159923553467

现在eratosthenes要快21倍。正如你所看到的,很明显eratosthenes要快得多。

使用numpy数组可以很容易地将内存减少32或64(取决于您的机器架构),并获得更快的结果:

>>> import numpy as np
>>> def erat2(n):
...     ar = np.ones(n, dtype=bool)
...     ar[0] = ar[1] = False
...     ar[4::2] = False
...     for j in xrange(3, n, 2):
...             if ar[j]:
...                     ar[j**2::2*j] = False
...     return ar.nonzero()[0]
... 
>>> timeit.timeit('erat2(10000)', 'from __main__ import erat2', number=1000)
0.5136890411376953

另一个比另一个筛子快4倍。

邵弘致
2023-03-14

你正在做的是试验除法 - 测试每个候选素数与它下面的每个已知素数。在调用范围中跳过奇数将为您节省一些除法,但这正是筛分所基于的技术:您知道每秒的数字都可以被2整除,因此是复合的。筛子只是将其扩展到:

  • 每三个数可以除以三,因此是复合数
  • 每五乘五,因此合成

诸如此类。由于Sieve被认为是最省时的算法之一,而trial division被认为是最省时的算法之一(维基百科将Sieve描述为< code>O(n log log n),根据下面的评论,您的算法可能是O(n^2 / log n)),因此有理由假设带有一些类似sieve的优化的trial division达不到筛分的要求。

 类似资料:
  • 我选择了“使用C进行编程原理和实践”,并且正在做一个涉及埃拉托色尼筛的早期问题,我有意想不到的输出,但我无法确定问题到底是什么。这是我的代码: 这个问题目前只要求我使用这种方法找到最大100的质数。我还尝试使用当前的“goto”方法在某些情况下跳出双循环,我还尝试在检查循环之后使用带有if语句的布尔标志,并简单地使用“继续”语句,但都没有任何效果。 (老实说,我想既然人们说goto是邪恶的,也许它

  • 我有一个问题,我有一个需要使用数组的赋值。我需要创建埃拉托斯特尼筛算法并打印出所有素数。我很困惑,因为据我所知,我的操作顺序是正确的。代码如下: 我首先将数组中的所有数字设置为true。然后第二个循环将从2开始“x”,然后在其中是一个嵌套循环,它将“x”乘以“n”的值,并且只要乘积(“y”)小于1000,“n”就会继续增加。一旦“y”达到最大值,“x”就会增加一个数字,重复该过程,直到所有非质数都

  • 我必须为'sieve of eratosthenes'算法编写一个java代码,以便在控制台上打印出给定最大值的素数,但我不被允许使用数组。我们的教授告诉我们,只有在循环的帮助下才能做到这一点。 所以我想了很多,在谷歌上搜索了很多关于这个话题的信息,但都找不到答案。我认为这根本不可能,因为你已经把那些数字已经被划掉的信息存储在某个地方了。 我的代码到现在为止: - 但是如果有一个解决方案,如果有人

  • 我正在解决欧拉项目的一些问题,必须生成200万质数来解决一个问题。我对埃拉托色尼筛的实现非常慢,但我不知道为什么。有人能解释一下这个实现的主要问题吗?我觉得它很漂亮,然后我发现它非常糟糕:(。我在网上找到了它的另一个实现,它比我的快得多。 编辑:感谢所有的答案!结论是过滤器是问题所在,因为它会遍历每个元素(而不仅仅是那些被标记为非素数的元素),而且每次都会创建一个新列表。用旧的循环和一轮过滤重写它

  • 我应该编写一个函数或脚本,找出所有小于给定整数n的质数p 我意识到这与我应该写的代码相去甚远。但这是我想到的唯一想法,它只删除了可被2和3整除的数字,我需要找到所有素数,对每个entry.This重复这一点显然不明智。但我觉得可以为此创建一个循环。但我没有编写这个循环。请帮助我。

  • 在一个类作业中,我被要求用Java编写Eratostenes筛选代码,我的代码效率非常低。运行时间不长,但我相当肯定,除了像我一样列出所有内容外,还有其他循环的空间。。 这是我的代码: 基本上,我所做的是将所有不是质数的元素设置为true… 所以我的主要2个问题是1。有没有办法实现一个循环,使代码更短2。如何打印此数组中所有为 true(质数)的元素