关于python:Erathostenes筛选优化

Erathostenes sieve optimization

本问题已经有最佳答案,请猛点这里访问。

几周前,我已经用python编写了erathostenes算法,它看起来像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def erathostenes(n):

    A = range(2,n+1)
    B = []
    i = 0

    while A[i] < math.sqrt(n):
        B.append(A[i])
        j = i
        aux = A[i]
        while j < len(A):
            if A[j]%aux == 0:
                A[j] = 0
            j += aux
        i += 1
        while A[i] == 0:
            i +=  1
    for i in range(len(A)):
        if A[i] != 0:
            B.append(A[i])
        i += 1
    return B

经过一番思考(我是编程新手),我只是对算法做了一些修改,现在看起来像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def erathostenes(n):

    A = range(2,n + 1)
    B = []
    i = 0

    raiz = math.sqrt(n)
    lenA = len(A)      
    rangeLenA = range(lenA)

    while A[i] < raiz:
        B.append(A[i])
        j = i
        aux = A[i]

        while j < lenA:
            A[j] = 0
            j += aux
        i += 1
        while A[i] == 0:
            i +=  1
    for i in rangeLenA:
        if A[i] != 0:
            B.append(A[i])
        i += 1
    return B

如果我以n = 10.000.000执行算法,则第一个代码中的执行时间约为7秒,第二个代码中的执行时间约为4秒。

关于我的算法中更多优化的想法吗? 谢谢!


1
i += 1

在最后一个循环中很有趣。

考虑更换

1
for i in rangeLenA:

1
for i in xrange(LenA)

您可以避免生成不需要的庞大列表。

编辑:

另外考虑一下:

1
    for j in xrange(i,lenA,aux):

代替:

1
    while j < lenA:

并修复错误

1
while A[i] <= raiz:

按照星期五的建议。


试图制作一个非循环版本只是为了好玩。结果是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def erathostenes(n):

    def helper_function(num_lst, acc):

        if not num_lst:
            return acc
        if len(num_lst) == 1:
            acc.append(num_lst[0])
            return acc
        num = num_lst.pop(0)
        multiples = ([x for x in range(num + 1, num_lst[-1] + 1)
                         if x % num == 0])

        remains = ([x for x in num_lst if x not in multiples])
        acc.append(num)
        return helper_function(remains, acc )
    return helper_function(range(2, n + 1), [])

当我运行计时时,post thothostenes(1000)得到826我们,我的版本(!!)得到26ms。令我惊讶的是它是如此缓慢。

函数式编程虽然更有趣,但是在Python中看起来并不适合解决此问题(我猜是使用功能性更强的语言会更快)。

所以我尝试了命令式版本。看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
def erathostenes_imperative(n):
    limit = int(math.sqrt(n))
    def helper_function(flags, size):
        for i in range(2,limit):
            if flags[i] == True:
                j = 2*i
                while j < size:
                    if j % i == 0:
                        flags[j] = False
                    j = j + i
        return [x for x in range(2, n + 1) if flags[x]]
    return helper_function([True]*(n + 1), n)

我所做的是将整数列表更改为True / False标志列表。直观地讲,迭代似乎更快,对吗?

我的结果是erathostenes_imperative(100000)为831ms,而您的版本为1.45。

可惜的是,命令式的编写速度更快。所有的fors,thes和i,j的代码看起来都很混乱


您的代码中有错误。更改

1
while A[i] < raiz:

1
while A[i] <= raiz:

当N为正方形时,您会发现错误。

为了优化,请将xrange用作rangeLenA而不是range。


试试阿特金筛子。相似,但是它是对Eratosthenes筛子的修改,并立即滤除了2、3、5的所有倍数以及其他一些优化。您可能还想尝试找到一种告诉您每个操作的运行时间的工具,并以较大的运行时间来修改这些操作。

但是,由于您不熟悉编程,因此最好为您提供实现其他算法或进行其他编程练习的机会。