这是我使用Sierat of Eratosthenes找到素数的代码。
list = [i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]
for i in list:
for a in list:
if a!=i and a%i == 0:
list.remove(a)
试图找到一种方法将那些嵌套for循环压缩成某种生成器或理解,但似乎你不能使用理解将函数应用于列表。我尝试使用地图和过滤器,但我似乎无法做到正确。
考虑这样的事情:
print map(list.remove(a), filter(lambda a, i: (a%i ==0 and a!=i), [(a, i) for i in list for a in list])
显然不会有十几个原因。如果我只是使用该代码的过滤器部分:
filter(lambda a, i: (a%i ==0 and a!=i), **[(a, i) for i in list for a in list]**
将两个变量放入lambda的正确方法是什么? (a,i)使它成为一个元组,但我想提交'a'和'i'作为自变量放入lambda。
我最终解决了这个单线问题:
print sorted(set([i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]).difference(a for i in l for a in l if a!=i and a%i == 0))
答案 0 :(得分:15)
首先要注意的是,你所写的不是eratosthenes的筛子。看看有多少循环是一个完全天真的eratosthenes筛子执行:
def sieve1(n):
loops = 0
numbers = set(range(2, n))
for i in range(2, int(n ** 0.5) + 1):
for j in range(i * 2, n, i):
numbers.discard(j)
loops += 1
return sorted(numbers), loops
测试:
>>> sieve1(100)
([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],
178)
包含100个数字的178个循环(不包括排序)。只需稍作修改即可改善这一点:
def sieve2(n):
loops = 0
numbers = range(0, n)
for prime in numbers:
if prime < 2:
continue
elif prime > n ** 0.5:
break
for i in range(prime ** 2, n, prime):
numbers[i] = 0
loops += 1
return [x for x in numbers if x > 1], loops
测试:
>>> sieve2(100)
([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],
102)
100个数字的102个循环(不包括末尾的过滤器)。现在看看你的:
def sieve3(n):
loops = 0
numbers = range(2, n)
for i in numbers:
for j in numbers:
if j != i and j % i == 0:
numbers.remove(j)
loops += 1
return numbers, loops
测试:
>>> sieve3(100)
([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],
663)
情况变得更糟:
>>> [sieve1(x)[1] for x in [100, 1000, 10000]]
[178, 2978, 41723]
>>> [sieve2(x)[1] for x in [100, 1000, 10000]]
[102, 1409, 16979]
>>> [sieve3(x)[1] for x in [100, 1000, 10000]]
[663, 28986, 1523699]
在n = 10000
,您的实施工作几乎可以完成100倍!
我的建议是在使其“紧凑”之前创建合理的实施方案。代码高尔夫可能很有趣,但无论长度如何,它都远不如编写高效代码那样具有挑战性或启发性。
那就是说,考虑这个单行(如果你不计算导入,你可以用lambda x, y: x - y
代替operator.sub
来除去导入。这实现了第一个算法,但改进很小:
>>> from operator import sub
>>> reduce(sub, (set(range(x ** 2, 100, x)) for x in range(2, int(100 ** 0.5) + 1)), set(range(2, 100)))
set([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])
答案 1 :(得分:5)
这不完全是你的循环的直接翻译,但它非常紧密和紧凑:
>>> l = range(2, 101)
>>> sorted(set(l).difference(a for i in l for a in l if a!=i and a%i == 0))
[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]
虽然我建议a > i
而不是a != 0
更短更快;)
答案 2 :(得分:3)
你没有做过Eratosthenes的筛子;不正确实施算法的危险在于它会非常慢。例如,在10**6
上尝试使用您的算法。
Eratosthenes有限筛子的最短实施我可以提出:
def primes(upTo):
isPrime = list(range(upTo))
for p in range(2,int(upTo**0.5)+1): #p: 2,3,4,...,sqrt(N)
print(p, isPrime[p])
if isPrime[p]:
for multiple in range(p**2,upTo,p): #mult: p^2, p^2+p, p^2+2p, ..., N
isPrime[multiple] = False
return [x for x in isPrime[2:] if x]
演示:
>>> list(primes(29))
[2, 3, 5, 7, 11, 13, 17, 19, 23]
如果你忽略了换行符和大量的跳过偶数数字优化,它实际上相当简洁:
isPrime=[True]*upTo for p in range(2,upTo): if isPrime[p]: yield p for m in range(p,upTo,p): isPrime[m]=False
答案 3 :(得分:0)
以下单行与您的代码完全无关:
def primes(n):
return set(range(2,n))-{c for i in range(2,n) for c in range(2*i,n,i)}
就像你的代码一样,这仍然是并非真正 Eratosthenes的筛子,因为,例如,它会徒劳地试图越过6
和9
等的倍数。然而,对于小于一百万或更多的值,它仍然比大多数其他Sieve相似的运行速度明显更快,因为对于小N,存在与“非素数”一样多的“素数”(数字的分数&lt; N是素数) 1/log(N)
)。
从源代码进行了大量修改,可能效率低于原始版本:http://codeblog.dhananjaynene.com/2011/06/10-python-one-liners-to-impress-your-friends/
答案 4 :(得分:0)
以下是筛子的简单演示。请注意,lambda不用作过滤函数,因为素数需要在定义时绑定。同样令人感兴趣的是它在不重复划分的意义上是有效的,但从长远来看它可能会导致you-know-what。
import itertools
def primes():
ints = itertools.count(2)
while True:
p = next(ints)
yield p
ints = itertools.ifilter(p.__rmod__, ints)
print list(itertools.islice(primes(), 10))
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
答案 5 :(得分:0)
def sieve(n):
sieve_list = range(n)
zero_list = [0] * n
for i in range(2, int(n**.5) + 1):
if sieve_list[i]:
sieve_list[2*i:n:i] = zero_list[2*i:n:i]
return filter(None, sieve_list)[1:]
答案 6 :(得分:0)
这是迄今为止我提出的最紧凑的真正筛子。这表现得非常好。
def pgen(n): # Sieve of Eratosthenes generator
np = set() # keeps track of composite (not prime) numbers
for q in xrange(2, n+1):
if q not in np:
yield q
np.update(range(q*q, n+1, q))
>>> list(pgen(100))
[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]
这个稍微复杂的版本是我见过的最快的版本:
def pgen(n): # Sieve of Eratosthenes generator by Dan Salmonsen
yield 2
np = set()
for q in xrange(3, n+1, 2):
if q not in np:
yield q
np.update(range(q*q, n+1, q+q))
这是一个真正的筛子作为列表理解:
def primes(n):
sieve = set(sum([range(q*q, n+1, q+q) for q in odds], []))
return [2] + [p for p in odds if p not in sieve]
>>> primes(100)
[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]
答案 7 :(得分:0)
并不是最紧凑的解决方案,但是Python3中range函数中的step参数在这里有帮助-
prime_sieve = [True] * (int(input('Primes Upto ?'))+1)
# The first prime number
for i in range(2, len(prime_sieve)):
if prime_sieve[i]:
for j in range(i+i, len(prime_sieve), i):
prime_sieve[j] = False
print(i, end=',')