【问题标题】:Erathostenes sieve optimization [duplicate]埃拉托色尼筛优化[重复]
【发布时间】:2013-10-26 15:04:34
【问题描述】:

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

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

经过一番思考(我是编程菜鸟),我只是对我的算法进行了一些修改,现在看起来像:

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 秒。

对我的算法进行更多优化有什么想法吗?谢谢!

【问题讨论】:

标签: python optimization


【解决方案1】:
i += 1 

最后一个循环很有趣。

考虑更换

for i in rangeLenA: 

for i in xrange(LenA) 

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

编辑:

还要考虑这个:

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

代替:

    while j < lenA:

并修复错误

while A[i] <= raiz: 

正如friday所建议的那样。

【讨论】:

【解决方案2】:

您的代码中有错误。改变

while A[i] < raiz:

while A[i] <= raiz:

当N为平方时,你会发现错误。

对于优化,使用 xrange 代替 range

【讨论】:

  • 我不知道 xrage 存在,感谢您提供的信息。我想,xrange 是更新版本的 range 吗?
  • range(n) 让您列出从 0 到 n-1 的列表,例如range(5) = (0, 1, 2, 3, 4) xrange(n) 为您提供 xrange 对象,该对象产生的值从列表相同到 range(n) 您可以阅读更多详细信息这里:docs.python.org/2/library/functions.html#xrange
【解决方案3】:

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

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), [])

当我运行计时时,posterathhostenes (1000) 得到 826 us,我的版本 (!!) 得到 26ms。让我感到惊讶,它是如此缓慢。

函数式编程更有趣,但看起来不适合这个问题,在 Python 中(我的猜测是在更函数式的语言中它会更快)。

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

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)

我所做的是将整数列表更改为真/假标志列表。直觉上,看起来迭代速度更快,对吧?

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

很遗憾,命令式编写它的速度更快。代码看起来很乱,包含所有的 fors、whiles、i 和 j

【讨论】:

  • 我不确定您的命令式版本是否完全正确。如果您执行 n=10.000.000,质数应该是 664579,而您的版本给出的结果是 664580(多一个)。让我们看看我能不能解决这个问题。谢谢!
  • 大声笑。刚刚编辑,编辑回来不要破坏。发现错误后,请随时编辑我的帖子!
【解决方案4】:

试试阿特金筛。它很相似,但它是对埃拉托色尼筛法的修改,它会立即过滤掉所有 2、3、5 的倍数,以及一些其他优化。您可能还想尝试找到一个工具来告诉您每个操作的运行时间,并使用更长的运行时间修改这些操作。

但是,由于您是编程新手,最好还是实现其他算法,或进行其他编程练习以提高您的能力。

【讨论】:

  • 必须指出的是,可以将轮分解应用于阿特金筛,就像它内置于阿特金筛一样,事实上,对于 SoE 可以使用更高级别的轮分解,而SoA 算法包含 2/3/5 轮子。具有最大轮子分解的 SoE 程序编写成与基本 SoA 相同的复杂度级别将比 SoA 运行得更快,在超过 10 亿的范围内运行,然后将对于“一个巨大的内存阵列”类型的实现,逐渐变慢,但这是针对可能不实用的巨大内存阵列大小。
猜你喜欢
  • 1970-01-01
  • 2015-09-16
  • 2011-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多