单行Python素发生器

时间:2012-05-17 16:41:20

标签: python math integer primes

我正在尝试在一行Python中创建素数生成器,这只是一个有趣的练习。

以下代码按预期工作,但速度太慢:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)])
for i in primes(10):
   print i,

所以我试着只检查j和k的平方根:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,int(round(math.sqrt(i)+1))) for k in xrange(1,int(round(math.sqrt(i)+1)))])
for i in primes(10):
   print i,

但它输出:2 3 5 6 7 8

所以我的指数j和k肯定有问题,但我没有线索。

11 个答案:

答案 0 :(得分:12)

这不是Eratosthenes的筛子,即使它看起来像。实际上情况要糟糕得多。 Sieve是寻找素数的最佳算法。

请参阅http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

编辑:我已将https://stackoverflow.com/a/9302299/711085修改为单行(原来它不是真正的Sieve,但现在它......可能......):< / p>

reduce( (lambda r,x: r-set(range(x**2,N,x)) if (x in r) else r), 
        range(2,N), set(range(2,N)))

演示:

>>> primesUpTo(N): lambda N: reduce(...)
>>> primesUpTo(30)
{2, 3, 5, 7, 11, 13, 17, 19}

遗憾的是,我认为虽然这在函数式编程语言中是有效的,但由于非持久性(共享状态和不可变)数据结构,它在python中可能效率不高,并且python中的任何筛都需要使用突变实现可比性能。如果我们迫切希望,我们仍然可以将它塞进一个单行程中。但首先......

普通筛:

>>> N = 100
>>> table = list(range(N))
>>> for i in range(2,int(N**0.5)+1):
...     if table[i]:
...         for mult in range(i**2,N,i):
...             table[mult] = False
... 
>>> primes = [p for p in table if p][1:]
>>> primes
[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]

我们现在可以在同一行上定义和调用匿名函数,以及[...].__setitem__进行内联变异的hack,以及... and foo在返回时评估...的hack foo

>>> primesUpTo = lambda N: (lambda table: [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] for i in range(2,int(N**0.5)+1) if table[i]] and [p for p in table if p][1:])(list(range(N)))
>>> primesUpTo(30)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

恐惧地继续畏缩,单线扩大(奇怪的美丽,因为你几乎可以直接翻译控制流,但对所有事情的可怕滥用):

lambda N:
    (lambda table: 
        [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] 
            for i in range(2,int(N**0.5)+1) if table[i]] 
        and [p for p in table if p][1:]
    )(list(range(N)))

这个单行变异版本在我的机器上放弃了大约10 8 ,而最初的变异版本放弃了大约10 9 ,内存不足(奇怪)。

原始reduce版本放弃了10 7 。所以也许毕竟 效率低下(至少对于你可以在你的计算机上处​​理的数字而言)。

edit2 您似乎可以更简洁地滥用副作用:

reduce( (lambda r,x: (r.difference_update(range(x**2,N,x)) or r)
                     if (x in r) else r), 
        range(2,N), set(range(2,N)))

它放弃了大约10 8 ,与单线性变异版本相同。

edit3: runs at O(N)empirical complexity,而difference_update没有O(n^2.2) complexity }。

将减少的范围限制为上限的sqrt和工作with odds only,两者都会导致额外的加速( 2x 1.6x < / em>相应地):

reduce( (lambda r,x: (r.difference_update(range(x*x,N,2*x)) or r)
                     if (x in r) else r), 
        range(3, int((N+1)**0.5+1), 2),
        set([2] + range(3,N,2)))

答案 1 :(得分:3)

您无法检查数字产品,只能检查平方根以测试素数。看看8- 8的平方根是2.8,所以它永远不会尝试4 * 2.(的确,不会被视为素数的唯一数字是方数)。

ETA:为什么不检查j是否可以被每个j(使用i % j == 0)整除到j的平方根,而不是尝试j和k的所有可能组合?这样既需要更少的代码,也更有效率(尽管它仍然不像Eratosthenes的Sieve那样高效)。

答案 2 :(得分:3)

这是你想要的:

def primes (q) :
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)])
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,j+1)])
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,j+1)])

 return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,min(j+1,i/j+1))])

在Haskell中,范围是包含的,因此primes(542)

[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],     k<-[1..n-1]]]  --  25.66s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],     k<-[1..j]]]    --  15.30s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..j]]]    --   6.00s
                                                                      --   0.79s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..min j (n`div`j)]]] 

实际上, 1*x == x 所以 1 不需要作为乘数,因此它应该是

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..min j (n`div`j)]]] 

仅需0.59秒。或者,在Python中,

def primes (q) :
 return (i for i in xrange(2,q) if i not in [j*k for j in xrange(2,i/2+1) for k in xrange(2,min(j+1,i/j+1))])

更新:出于某种原因,min j ...至少在Haskell中没有太大作用。所以表达式变得简单

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..n`div`j]]] 

答案 3 :(得分:1)

怎么样:

def primes(x):
  return [i for i in range(2,x) if 0 not in [i%j for j in range(2,i)]]

答案 4 :(得分:0)

使用理解力

[x for x in range(4, 1000) if all(x % y != 0 for y in range(2, int(math.sqrt(x)) + 1))]

答案 5 :(得分:0)

def isPrime(n):
    return all(n % d for d in range(2, int(n**.5)+1))

primes = set(n for n in range(2, 100) if isPrime(n))

如果需要,可以将其压缩为一行:

primes = set(n for n in range(2, 100) if all(n % d for d in range(2,int(n**.5)+1)))

all()处确保所有值都不是False (或0;在这种情况下,可以确保我们测试的数字以下的数字都不是因数,在这种情况下,数字为质数。

我们检查所有因素,直至效率的平方根为止:如果没有在sqrt(n)以下的数字是因素,则在sqrt(n)以上的数字都不是因素,因为因素总是成对出现。

答案 6 :(得分:0)

尽管这里还有其他一些很好的答案,但我还是希望包含它的版本。

n = 1000
primes = [candidate for candidate in range(1, n) if candidate not in [x * mult for x in range(2,n // 2 + 1) for mult in range(2,n//x)]]

运行时间为O(n ** 2)。

答案 7 :(得分:0)

下面是执行此操作的一行代码:

print((lambda n:[i for i in range(2, n) if ["A7A" for j in range(2, i) if i%j==0]==[]])(100))

答案 8 :(得分:0)

这是另一种不是单行但更有效的方式。积极的一面是,每次你只检查以前找到的素数而不是查看 range(n) 中的所有值:

def prime(n):
    pm_list = [2]
    for i in range(3,n+1):
        pm_arr = np.array(pm_list)
        if any(i // pm_arr == i / pm_arr) == False:
            pm_list.append(i)
    return pm_list

答案 9 :(得分:0)

这是一个基于埃拉托色尼筛法的单线。

primes = lambda N: (N>1)*[2]+[p for s in [[1]*(N+1)] for p in range(3,N+1,2) if s[p] and not s.__setitem__(slice(p,None,p),N//p*[0])]

它在我的笔记本电脑上在 0.097 秒内生成多达 1,000,000 个素数。

对于长达 10^8 的素数:11.6 秒,

对于 10^9 以内的素数:169.3 秒(2.8 分钟)

对于高达 10^10 的素数:似乎内存不足(IDLE shell 退出)

答案 10 :(得分:-1)

我的代码是(对于范围内的数字(2,50)):

import operator

[len(x) for x in list(map(lambda x: [operator.mod(len(range(1,x)), z) for z in range(1,x)], [item for item in range(2,50)])) if x.count(0) == 2]