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的所有倍数以及其他一些优化。您可能还想尝试找到一种告诉您每个操作的运行时间的工具,并以较大的运行时间来修改这些操作。
但是,由于您不熟悉编程,因此最好为您提供实现其他算法或进行其他编程练习的机会。