【问题标题】:Python prime generator in one-line单行中的 Python 素数生成器
【发布时间】:2012-05-25 06:34:59
【问题描述】:

我正在尝试在一行 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 一定有问题,但我不知道。

【问题讨论】:

标签: python math integer primes


【解决方案1】:

这不是埃拉托色尼筛法,尽管看起来确实如此。事实上,情况要糟糕得多。 Sieve 是寻找素数的最佳算法。

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

编辑:我已将https://stackoverflow.com/a/9302299/711085 修改为单线(最初它不是真正的 Sieve,但现在...可能...):

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)))

在我的机器上,这个单行变异版本在 108 左右放弃,而原始变异版本在 109 左右放弃,内存不足(奇怪)。

原来的reduce版本在107放弃了。所以也许它毕竟不是效率低下(至少对于你可以在你的计算机上处​​理的数字)。

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)))

它在 108 左右放弃,与单线变异版本相同。

edit3: 这个runs at O(N) empirical complexity,而没有difference_update 它运行在O(n^2.2) complexity

将减少的范围限制为上限的 sqrt,并工作 with odds only,两者都会导致额外的加速(2x1.6x 对应):

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)))

【讨论】:

  • 是的,但这并不能解决他的单行代码中的错误。
  • @DavidRobinson:他似乎更关心他的回答速度,这与算法直接相关。相似算法在 10000+ 时会开始感觉很慢,在 1000000+ 时会失败。无论如何,我已经编辑了我的答案以给出一个真正的单行。
  • (如果有人想知道为什么我一直在编辑/取消编辑,那是因为很难判断一个非正统的 Sieve 实现是否真的有 en.wikipedia.org/wiki/… 的运行时间;现在仍然可以确定。 .. 遗憾的是,我认为虽然这在函数式编程语言中会很有效,但由于非共享数据结构,它在 python 中效率不高,python 中的任何筛子都需要使用突变)
  • 实际上存在更好的筛子来寻找素数。我相信目前的冠军是阿特金斯的筛子。但埃拉托色尼几乎可以满足您的任何需求。
  • @MarkRansom 不,这才是真正的埃拉托色尼筛法所做的,它确实删除了多次的倍数 - 每个数字的质数一次因素。那是因为在处理数组时它不会“删除”它们 - 它标记它们关闭。从数组中删除一个条目会破坏随机访问,这是筛子效率的关键——每个 mark-off 需要 O(1) 时间。对于集合,它可能是 O(log(size-of-set)),所以更糟一点。并且交叉range(i**2,N,2*i) 的改进确实需要编辑,恕我直言(尤其是带套)。 :)(请注意,仅使用赔率时是2*i)。
【解决方案2】:

您不能只检查直到平方根的数字乘积来测试素数。看看 8 - 8 的平方根是 2.8,所以它永远不会尝试 4 * 2。(事实上,唯一不会被视为素数的数字是平方数。

ETA:与其尝试所有可能的 j 和 k 组合,不如检查 i 是否可以被每个 j 整除(使用i % j == 0)直到 j 的平方根?这既需要更少的代码,也更高效(尽管它仍然远没有像埃拉托色尼筛法那样高效)。

【讨论】:

    【解决方案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]]] 
    

    【讨论】:

      【解决方案4】:

      怎么样:

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

      【讨论】:

      • 您可以将其改写为: def primes(x): return [i for i in range(2,x) if all([i%j for j in range(2,i) )])]
      • 如果数字大于 1,您应该添加一个案例,如下所示:[i for i in range(2,x) if ((0 not in [i%j for j in range(2,i)]) and i &gt; 1)]
      • 为什么?它被i in range(2,x)覆盖
      【解决方案5】:

      使用推导

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

      【讨论】:

        【解决方案6】:

        虽然这里还有其他很好的答案,但我想包括我的版本。

        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】:
          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) 以上的数字都不是因子,因为因子总是成对出现。

          【讨论】:

            【解决方案8】:

            这里有一行代码:

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

            【讨论】:

              【解决方案9】:

              这是另一种方式,它不是单行但更有效。积极的一面是,每次您只检查以前找到的素数而不是查看 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
              

              【讨论】:

                【解决方案10】:

                这是一个基于 Eratosthenes 筛子的单衬。

                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 退出)

                【讨论】:

                  【解决方案11】:

                  以下代码应打印低于“limit”值的所有数字的列表。

                  primes = lambda limit: [num for num in range(2, limit) if num > 1 and (num == 2 or num % 2 != 0) and all(num % divisor != 0 for divisor in range(3, int(num ** 0.5) + 1, 2))]
                  
                  print(primes(30))
                  

                  【讨论】:

                    【解决方案12】:

                    我的代码是(对于范围(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]
                    

                    【讨论】:

                    • 您好,您可以将您的陈述表述为一个问题吗?你是想问如何在一行中制作一个python素数生成器?
                    • @YacineMahdid 这篇文章是一个答案,而不是一个问题
                    猜你喜欢
                    • 2010-10-08
                    • 2013-03-20
                    • 1970-01-01
                    • 2019-09-06
                    • 2015-06-30
                    • 1970-01-01
                    • 2015-02-20
                    • 2013-02-25
                    • 2013-07-24
                    相关资源
                    最近更新 更多