只是为了澄清,这不是一个家庭作业问题:)
我想为我正在建造的数学应用找到素数。遇到了Sieve of Eratosthenes方法。
我已经用Python编写了它的实现。但它非常慢。比方说,如果我想找到不到200万的所有素数。它需要> 20分钟(此时我停了下来)。我怎样才能加快速度呢?
def primes_sieve(limit):
limitn = limit+1
primes = range(2, limitn)
for i in primes:
factors = range(i, limitn, i)
for f in factors[1:]:
if f in primes:
primes.remove(f)
return primes
print primes_sieve(2000)
更新 我最终对这段代码进行了分析。发现花了很多时间从列表中删除一个元素。考虑到它必须遍历整个列表(最坏情况)以找到元素和放大器,这是相当容易理解的。然后删除它,然后重新调整列表(也许一些副本继续?)。无论如何,我把字典列表删掉了。我的新实现 -
def primes_sieve1(limit):
limitn = limit+1
primes = dict()
for i in range(2, limitn): primes[i] = True
for i in primes:
factors = range(i,limitn, i)
for f in factors[1:]:
primes[f] = False
return [i for i in primes if primes[i]==True]
print primes_sieve1(2000000)
答案 0 :(得分:98)
你没有完全实现正确的算法:
在你的第一个例子中,primes_sieve
没有维护要打开/取消设置的素数标记列表(如在算法中),而是连续调整整数列表,这非常昂贵:删除项目从列表中需要将所有后续项目减少一个。
在第二个例子中,primes_sieve1
维护一个pr emity标志的字典,这是向正确方向迈出的一步,但它以未定义的顺序迭代字典,并且冗余地敲出因子因素(而不仅仅是素数因子,如算法中所示)。您可以通过对键进行排序和跳过非素数(已经使其快一个数量级)来解决这个问题,但是直接使用列表仍然更有效。
正确的算法(使用列表而不是字典)看起来像:
def primes_sieve2(limit):
a = [True] * limit # Initialize the primality list
a[0] = a[1] = False
for (i, isprime) in enumerate(a):
if isprime:
yield i
for n in range(i*i, limit, i): # Mark factors non-prime
a[n] = False
(请注意,这还包括在素数平方(i*i
)处开始非素数标记的算法优化,而不是其双倍。)
答案 1 :(得分:11)
def eratosthenes(n):
multiples = []
for i in range(2, n+1):
if i not in multiples:
print (i)
for j in range(i*i, n+1, i):
multiples.append(j)
eratosthenes(100)
答案 2 :(得分:6)
从数组(列表)的开头删除需要将所有项目向下移动。这意味着从前面开始以这种方式从列表中删除每个元素是O(n ^ 2)操作。
使用集合可以更有效地完成这项工作:
def primes_sieve(limit):
limitn = limit+1
not_prime = set()
primes = []
for i in range(2, limitn):
if i in not_prime:
continue
for f in range(i*2, limitn, i):
not_prime.add(f)
primes.append(i)
return primes
print primes_sieve(1000000)
...或者,避免重新排列列表:
def primes_sieve(limit):
limitn = limit+1
not_prime = [False] * limitn
primes = []
for i in range(2, limitn):
if not_prime[i]:
continue
for f in xrange(i*2, limitn, i):
not_prime[f] = True
primes.append(i)
return primes
答案 3 :(得分:1)
我制作了一个单衬版的 Eratosthenes 筛
sieve = lambda j: [print(x) for x in filter(lambda n: 0 not in map(lambda i: n % i, range(2, n)) and (n!=1)&(n!=0), range(j + 1))]
就性能而言,我很确定这无论如何都不是最快的,而就可读性/遵循 PEP8 而言,这非常糟糕,但它的长度比任何东西都更新颖。
>编辑:请注意,这只是打印筛子并不会返回(如果您尝试打印它,您将获得一个无列表,如果您想返回,请将列表理解中的打印(x)更改为“ x”。
答案 4 :(得分:1)
我刚刚想到了这个。它可能不是最快的,但是除了直接添加和比较之外,我没有使用其他任何东西。当然,您在这里遇到的是递归限制。
def nondivsby2():
j = 1
while True:
j += 2
yield j
def nondivsbyk(k, nondivs):
j = 0
for i in nondivs:
while j < i:
j += k
if j > i:
yield i
def primes():
nd = nondivsby2()
while True:
p = next(nd)
nd = nondivsbyk(p, nd)
yield p
def main():
for p in primes():
print(p)
答案 5 :(得分:1)
使用numpy
,我可以在2秒多的时间内找到所有低于1亿的质数。
有两个主要特征之一,应该注意
i
截取i
的倍数,直到n
的根为止i
将False
设置为x[2*i::i] = False
的倍数比使用显式python for循环要快得多。这两个可以大大加快您的代码的速度。对于一百万以下的限制,没有可察觉的运行时间。
import numpy as np
def primes(n):
x = np.ones((n+1,), dtype=np.bool)
x[0] = False
x[1] = False
for i in range(2, int(n**0.5)+1):
if x[i]:
x[2*i::i] = False
primes = np.where(x == True)[0]
return primes
print(len(primes(100_000_000)))
答案 6 :(得分:1)
通过结合许多爱好者的贡献(包括上面评论中的Glenn Maynard和MrHIDEn),我在python 2中提出了以下代码:
def simpleSieve(sieveSize):
#creating Sieve.
sieve = [True] * (sieveSize+1)
# 0 and 1 are not considered prime.
sieve[0] = False
sieve[1] = False
for i in xrange(2,int(math.sqrt(sieveSize))+1):
if sieve[i] == False:
continue
for pointer in xrange(i**2, sieveSize+1, i):
sieve[pointer] = False
# Sieve is left with prime numbers == True
primes = []
for i in xrange(sieveSize+1):
if sieve[i] == True:
primes.append(i)
return primes
sieveSize = input()
primes = simpleSieve(sieveSize)
对于功率为10的不同输入,我的机器上计算的时间是:
答案 7 :(得分:1)
快得多:
import time
def get_primes(n):
m = n+1
#numbers = [True for i in range(m)]
numbers = [True] * m #EDIT: faster
for i in range(2, int(n**0.5 + 1)):
if numbers[i]:
for j in range(i*i, m, i):
numbers[j] = False
primes = []
for i in range(2, m):
if numbers[i]:
primes.append(i)
return primes
start = time.time()
primes = get_primes(10000)
print(time.time() - start)
print(get_primes(100))
答案 8 :(得分:1)
我意识到这并没有真正回答如何快速生成素数的问题,但也许有些人会发现这个替代方案很有意思:因为python通过生成器提供了懒惰的评估,eratosthenes&#39;筛子可以完全按照规定实施:
def intsfrom(n):
while True:
yield n
n += 1
def sieve(ilist):
p = next(ilist)
yield p
for q in sieve(n for n in ilist if n%p != 0):
yield q
try:
for p in sieve(intsfrom(2)):
print p,
print ''
except RuntimeError as e:
print e
try块就在那里,因为算法运行直到它吹掉堆栈而没有 尝试阻止显示回溯,按下你想要离开屏幕的实际输出。
答案 9 :(得分:0)
我的实施:
import math
n = 100
marked = {}
for i in range(2, int(math.sqrt(n))):
if not marked.get(i):
for x in range(i * i, n, i):
marked[x] = True
for i in range(2, n):
if not marked.get(i):
print i
答案 10 :(得分:0)
这是一个更节省内存的版本(并且:适当的筛选,而不是试验分区)。基本上,不是保留所有数字的数组,而是交叉那些不是素数的数组,而是保留一组计数器 - 一个用于发现它的每个素数 - 并在推定的素数之前跳跃它们。这样,它使用与素数成比例的存储,而不是最高素数。
import itertools
def primes():
class counter:
def __init__ (this, n): this.n, this.current, this.isVirgin = n, n*n, True
# isVirgin means it's never been incremented
def advancePast (this, n): # return true if the counter advanced
if this.current > n:
if this.isVirgin: raise StopIteration # if this is virgin, then so will be all the subsequent counters. Don't need to iterate further.
return False
this.current += this.n # pre: this.current == n; post: this.current > n.
this.isVirgin = False # when it's gone, it's gone
return True
yield 1
multiples = []
for n in itertools.count(2):
isPrime = True
for p in (m.advancePast(n) for m in multiples):
if p: isPrime = False
if isPrime:
yield n
multiples.append (counter (n))
您会注意到primes()
是一个生成器,因此您可以将结果保存在列表中,也可以直接使用它们。这是第一个n
素数:
import itertools
for k in itertools.islice (primes(), n):
print (k)
而且,为了完整起见,这里有一个衡量绩效的计时器:
import time
def timer ():
t, k = time.process_time(), 10
for p in primes():
if p>k:
print (time.process_time()-t, " to ", p, "\n")
k *= 10
if k>100000: return
万一你想知道,我还写了primes()
作为一个简单的迭代器(使用__iter__
和__next__
),它以几乎相同的速度运行。也让我很惊讶!
答案 11 :(得分:0)
因为速度,我更喜欢NumPy。
http://reportServerName/reports/powerbi/PowerBIReport?rs:embed=true
检查输出:
import numpy as np
# Find all prime numbers using Sieve of Eratosthenes
def get_primes1(n):
m = int(np.sqrt(n))
is_prime = np.ones(n, dtype=bool)
is_prime[:2] = False # 0 and 1 are not primes
for i in range(2, m):
if is_prime[i] == False:
continue
is_prime[i*i::i] = False
return np.nonzero(is_prime)[0]
# Find all prime numbers using brute-force.
def isprime(n):
''' Check if integer n is a prime '''
n = abs(int(n)) # n is a positive integer
if n < 2: # 0 and 1 are not primes
return False
if n == 2: # 2 is the only even prime number
return True
if not n & 1: # all other even numbers are not primes
return False
# Range starts with 3 and only needs to go up the square root
# of n for all odd numbers
for x in range(3, int(n**0.5)+1, 2):
if n % x == 0:
return False
return True
# To apply a function to a numpy array, one have to vectorize the function
def get_primes2(n):
vectorized_isprime = np.vectorize(isprime)
a = np.arange(n)
return a[vectorized_isprime(a)]
比较Eratosthenes的筛子速度和Jupyter笔记本上的蛮力。 Eratosthenes的筛子比百万元素的蛮力快539倍。
n = 100
print(get_primes1(n))
print(get_primes2(n))
[ 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]
[ 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]
答案 12 :(得分:0)
我认为必须可以简单地使用空列表作为循环的终止条件并提出这个:
limit = 100
ints = list(range(2, limit)) # Will end up empty
while len(ints) > 0:
prime = ints[0]
print prime
ints.remove(prime)
i = 2
multiple = prime * i
while multiple <= limit:
if multiple in ints:
ints.remove(multiple)
i += 1
multiple = prime * i
答案 13 :(得分:0)
import math
def sieve(n):
primes = [True]*n
primes[0] = False
primes[1] = False
for i in range(2,int(math.sqrt(n))+1):
j = i*i
while j < n:
primes[j] = False
j = j+i
return [x for x in range(n) if primes[x] == True]
答案 14 :(得分:0)
我最快的实施方式:
isprime = [True]*N
isprime[0] = isprime[1] = False
for i in range(4, N, 2):
isprime[i] = False
for i in range(3, N, 2):
if isprime[i]:
for j in range(i*i, N, 2*i):
isprime[j] = False
答案 15 :(得分:0)
我认为这是用eratosthenes方法查找素数的最短代码
def prime(r):
n = range(2,r)
while len(n)>0:
yield n[0]
n = [x for x in n if x not in range(n[0],r,n[0])]
print(list(prime(r)))
答案 16 :(得分:0)
不确定我的代码是否有效,有人愿意评论吗?
from math import isqrt
def isPrime(n):
if n >= 2: # cheating the 2, is 2 even prime?
for i in range(3, int(n / 2 + 1),2): # dont waste time with even numbers
if n % i == 0:
return False
return True
def primesTo(n):
x = [2] if n >= 2 else [] # cheat the only even prime
if n >= 2:
for i in range(3, n + 1,2): # dont waste time with even numbers
if isPrime(i):
x.append(i)
return x
def primes2(n): # trying to do this using set methods and the "Sieve of Eratosthenes"
base = {2} # again cheating the 2
base.update(set(range(3, n + 1, 2))) # build the base of odd numbers
for i in range(3, isqrt(n) + 1, 2): # apply the sieve
base.difference_update(set(range(2 * i, n + 1 , i)))
return list(base)
print(primesTo(10000)) # 2 different methods for comparison
print(primes2(10000))
答案 17 :(得分:0)
简单的速度攻击:当您定义变量“primes”时,将步长设置为2以自动跳过所有偶数,并将起点设置为1.
然后你可以进一步优化而不是素数中的i,在素数中使用i [:round(len(primes)** 0.5)]。这将大大提高性能。此外,您可以消除以5结尾的数字,以进一步提高速度。
答案 18 :(得分:0)
获取主数的最快方法是:
import sympy
list(sympy.primerange(lower, upper+1))
万一您不需要存储它们,只需使用上面的代码即可,无需转换为list
。 sympy.primerange
是生成器,因此不占用内存。
答案 19 :(得分:0)
使用递归和海象运算符:
def prime_factors(n):
for i in range(2, int(n ** 0.5) + 1):
if (q_r := divmod(n, i))[1] == 0:
return [i] + factor_list(q_r[0])
return [n]