【问题标题】:generator in Python generating prime numbersPython中的生成器生成素数
【发布时间】:2013-03-20 08:54:14
【问题描述】:

我需要在 Python 中使用生成器生成素数。这是我的代码:

def genPrimes():
    yield 2
    x=2
    while True:
        x+=1
        for p in genPrimes():
            if (x%p)==0:
                break
        else:
            yield x

我有一个 RuntimeError: 在我运行它时,在第二个 prime.next() 之后超出了最大递归深度。

【问题讨论】:

标签: python generator primes


【解决方案1】:

genPrimes() 无条件地使用完全相同的参数调用自己。这会导致无限递归。

这是使用(非递归)生成器的一种方法:

def gen_primes():
    n = 2
    primes = set()
    while True:
        for p in primes:
            if n % p == 0:
                break
        else:
            primes.add(n)
            yield n
        n += 1

请注意,这是为了简单明了而不是性能而优化的。

【讨论】:

  • 谢谢。 primes=gen_primes() 与 primes=set() 相比有何作用?
  • set 是否保证按照添加到集合中的顺序将其内容生成到for 枚举?
  • @WillNess:我不这么认为,但我不明白这有多重要。
  • 谢谢。只对效率有影响,但正如你所说,这里不关心,所以这里无所谓。
  • 在这个版本上它当然没关系,因为它有一个 much 更大的问题,而不是停留在数字的平方根测试。显然,它不能停止,因为素数是无序的。 :)
【解决方案2】:

找到素数的好方法。 n 是停止搜索的上限。

def prime(i, primes):
    for prime in primes:
        if not (i == prime or i % prime):
            return False
    primes.add(i)
    return i

def find_primes(n):
    primes = set([2])
    i, p = 2, 0
    while True:
        if prime(i, primes):
            p += 1
            if p == n:
                return primes
        i += 1

【讨论】:

    【解决方案3】:

    生成素数的最快方法是使用筛子。这里我们使用 Eratosthenes 的分段筛来生成素数,一个一个没有最大值,按顺序生成; ps 是筛选出小于当前最大值的素数列表,qs 是当前段中对应ps 的最小倍数的偏移量。

    def genPrimes():
        def isPrime(n):
            if n % 2 == 0: return n == 2
            d = 3
            while d * d <= n:
                if n % d == 0: return False
                d += 2
            return True
        def init(): # change to Sieve of Eratosthenes
            ps, qs, sieve = [], [], [True] * 50000
            p, m = 3, 0
            while p * p <= 100000:
                if isPrime(p):
                    ps.insert(0, p)
                    qs.insert(0, p + (p-1) / 2)
                    m += 1
                p += 2
            for i in xrange(m):
                for j in xrange(qs[i], 50000, ps[i]):
                    sieve[j] = False
            return m, ps, qs, sieve
        def advance(m, ps, qs, sieve, bottom):
            for i in xrange(50000): sieve[i] = True
            for i in xrange(m):
                qs[i] = (qs[i] - 50000) % ps[i]
            p = ps[0] + 2
            while p * p <= bottom + 100000:
                if isPrime(p):
                    ps.insert(0, p)
                    qs.insert(0, (p*p - bottom - 1)/2)
                    m += 1
                p += 2
            for i in xrange(m):
                for j in xrange(qs[i], 50000, ps[i]):
                    sieve[j] = False
            return m, ps, qs, sieve
        m, ps, qs, sieve = init()
        bottom, i = 0, 1
        yield 2
        while True:
            if i == 50000:
                bottom = bottom + 100000
                m, ps, qs, sieve = advance(m, ps, qs, sieve, bottom)
                i = 0
            elif sieve[i]:
                yield bottom + i + i + 1
                i += 1
            else: i += 1
    

    使用试除法的简单isPrime 就足够了,因为它将被限制在n 的第四根。段大小2 * delta 任意设置为 100000。此方法需要 O(sqrt n) 空间用于筛分素数加上恒定空间用于筛子。

    使用轮子生成候选素数并基于对基数 2、7 和 61 的强伪素数测试使用 isPrime 测试候选素数的速度较慢但节省空间;这对 2^32 有效。

    def genPrimes(): # valid to 2^32
        def isPrime(n):
            def isSpsp(n, a):
                d, s = n-1, 0
                while d % 2 == 0:
                    d /= 2; s += 1
                t = pow(a,d,n)
                if t == 1: return True
                while s > 0:
                    if t == n-1: return True
                    t = (t*t) % n; s -= 1
                return False
            for p in [2, 7, 61]:
                if n % p == 0: return n == p
                if not isSpsp(n, p): return False
            return True
        w, wheel = 0, [1,2,2,4,2,4,2,4,6,2,6,4,2,4,\
            6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,\
            2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10]
        p = 2; yield p
        while True:
            p = p + wheel[w]
            w = 4 if w == 51 else w + 1
            if isPrime(p): yield p
    

    如果您对使用素数进行编程感兴趣,我在我的博客中谦虚地推荐 this essay

    【讨论】:

    • isSpsp 的其他短碱基列表及其相应的有效范围是什么?在哪里可以找到那些?谢谢。
    • @WillNess:“最佳解决方案”是愚弄测试的最小数字。例如,给定三个素数集 2、7、61,测试报告为素数的最小合数是 4759123141。否则它是不会欺骗测试的最大数。我忘了哪个,但你很容易检查。 Charles Greathouse 和 Jeff Gilchrist 在这方面也做过工作,如果你想用谷歌搜索他们的网站,但如果你只是想得到答案,我链接的页面是最简单的地方。
    • 谢谢!还在您提到的那个页面链接到的 WP 页面中找到了“小”数字的简单列表。 :)
    【解决方案4】:

    非常不错!当达到x 的平方根时,您只是忘记停止从内部genPrimes() 取素数。就这样。

    def genPrimes():
        yield 2 
        x=2
        while True:
            x+=1
            for p in genPrimes():
                if p*p > x:        # 
                    yield x        #
                    break          # 
                if (x%p)==0:
                    break
            # else:
            #    yield x
    

    没有它,你会滑出深渊,或者是什么表情。当蛇吃自己的尾巴时,它必须停在中间。到头了,就没有蛇了(嗯,这里吃的方向是相反的,不过还是适用的……)。

    【讨论】:

      【解决方案5】:

      再简洁一点:

      import itertools
      
      def check_prime(n, primes):
          for p in primes:
              if not n % p:
                  return False
          return True
      
      def prime_sieve():
          primes = set()
          for n in itertools.count(2):
              if check_prime(n, primes):
                  primes.add(n)
                  yield n
      

      你可以这样使用它:

      g = prime_sieve()
         g.next()
      => 2
         g.next()
      => 3
         g.next()
      => 5
         g.next()
      => 7
         g.next()
      => 11
      

      【讨论】:

        【解决方案6】:

        这是一个快速而清晰的命令式素数生成器,不使用筛子:

        def gen_primes():
            n = 2
            primes = []
            while True:
                is_prime = True
                for p in primes:
                    if p*p > n:
                        break
                    if n % p == 0:
                        is_prime = False
                        break
                if is_prime:
                    primes.append(n)
                    yield n
                n += 1
        

        这与 NPE 的答案几乎相同,但包括一个根测试,可显着加快大素数的生成。结果是primes 列表的O(n) 空间使用量。

        【讨论】:

          【解决方案7】:

          这是一个生成从 2 到给定数的素数列表的脚本

          from math import sqrt
          next = 2
          n = int(raw_input())
          y = [i for i in range(2, n+1)]
          while y.index(next)+1 != len(y) and next < sqrt(n):
              y = filter(lambda x: x%next !=0 or x==next, y)
              next = y[y.index(next)+1]
          print y
          

          这只是Sieve of Eratosthenes 的另一个实现。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-09-06
            • 2011-10-06
            • 2015-06-30
            • 2012-05-25
            • 2010-10-08
            • 1970-01-01
            • 2013-10-02
            • 2015-02-20
            相关资源
            最近更新 更多