【问题标题】:Basic prime number generator in PythonPython中的基本素数生成器
【发布时间】:2015-06-30 23:57:58
【问题描述】:

只是想对我的素数生成器提供一些反馈。例如可以吗,它是否使用了很多资源等等。它不使用任何库,它相当简单,并且它反映了我目前的编程技能状态,所以不要因为我想学习而退缩。

def prime_gen(n):

    primes = [2]
    a = 2 

    while a < n:

        counter = 0 

        for i in primes:
            if a % i == 0:
                counter += 1

        if counter == 0:
            primes.append(a)
        else:
            counter = 0

        a = a + 1

    print primes

【问题讨论】:

标签: python


【解决方案1】:

有一些常见的优化:

示例:

def prime(x):
    if x in [0, 1]:
        return False
    if x == 2:
        return True
    for n in xrange(3, int(x ** 0.5 + 1)):
        if x % n == 0:
            return False
    return True
  • 涵盖基本案例
  • 只迭代到 n 的平方根

上面的例子不生成素数,而是测试它们。您可以对您的代码进行相同的优化:)

我发现用 Python 编写的一种更有效的算法可以在以下问题和答案中找到(使用筛子):

Simple Prime Generator in Python

我自己对筛算法的改编:

from itertools import islice


def primes():
    if hasattr(primes, "D"):
        D = primes.D
    else:
        primes.D = D = {}

    def sieve():
        q = 2
        while True:
            if q not in D:
                yield q
                D[q * q] = [q]
            else:
                for p in D[q]:
                    D.setdefault(p + q, []).append(p)
                del D[q]

            q += 1

    return sieve()


print list(islice(primes(), 0, 1000000))

在我的硬件上,我可以很快生成前一百万个素数(鉴于这是用 Python 编写的):

prologic@daisy
Thu Apr 23 12:58:37 
~/work/euler
$ time python foo.py > primes.txt

real    0m19.664s
user    0m19.453s
sys 0m0.241s

prologic@daisy
Thu Apr 23 12:59:01 
~/work/euler
$ du -h primes.txt
8.9M    primes.txt

【讨论】:

  • 对于测试,最好明确检查 2,然后在 xrange(3, int(x ** 0.5 + 1), 2) 上进行迭代。检查所有偶数没有意义。
【解决方案2】:

这是我不久前写的一个非常有效的素数生成器,它使用Sieve of Eratosthenes

#!/usr/bin/env python2.7

def primeslt(n):
    """Finds all primes less than n"""

    if n < 3:
        return []

    A = [True] * n
    A[0], A[1] = False, False

    for i in range(2, int(n**0.5)+1):
        if A[i]:
            j = i**2
            while j < n:
                A[j] = False
                j += i

    return [num for num in xrange(n) if A[num]]

def main():
    i = ''
    while not i.isdigit():
        i = raw_input('Find all prime numbers less than... ')
    print primeslt(int(i))

if __name__ == '__main__':
    main()

维基百科的文章(上面链接)解释了它是如何比我做得更好的,所以我只是建议你阅读它。

【讨论】:

    【解决方案3】:

    这是从 C# 版本改编而来的生成素数的标准方法:Most Elegant Way to Generate Prime Number

    def prime_gen(n):
    
        primes = [2]
    
        # start at 3 because 2 is already in the list
        nextPrime = 3
    
        while nextPrime < n:
    
            isPrime = True
    
            i = 0
    
            # the optimization here is that you're checking from
            # the number in the prime list to the square root of
            # the number you're testing for primality
            squareRoot = int(nextPrime ** .5)
    
            while primes[i] <= squareRoot:
    
                if nextPrime % primes[i] == 0:
    
                    isPrime = False
    
                i += 1
    
            if isPrime:
    
                primes.append(nextPrime)
    
            # only checking for odd numbers so add 2
            nextPrime += 2
    
        print primes
    

    【讨论】:

      【解决方案4】:

      我对第一个代码进行了一些优化,可以在参数为负时使用:

      def is_prime(x):    
          if x <=1:
              return False
          else:
              for n in xrange(2, int(x ** 0.5 + 1)):
                  if x % n == 0:
                      return False
          return True
      print is_prime(-3)
      

      【讨论】:

        【解决方案5】:

        作为 Python,通常最好返回一个生成器,该生成器将返回一个无限的素数序列而不是一个列表。

        ActiveState 有一个旧的 Eratosthenes 筛子列表recipes

        这是其中一个使用itertools count 更新到 Python 2.7 的方法,其中一个 step 参数在编写原始配方时不存在:

        import itertools as it
        
        def sieve():
            """ Generate an infinite sequence of prime numbers.
            """
            yield 2
            D = {}
            for q in it.count(3, 2):   # start at 3 and step by odds
                p = D.pop(q, 0)
                if p:
                    x = q + p
                    while x in D: x += p
                    D[x] = p          # new composite found. Mark that
                else:
                    yield q           # q is a new prime since no composite was found
                    D[q*q] = 2*q
        

        由于它是一个生成器,它比生成整个列表更节省内存。由于它定位复合材料,因此它的计算效率也很高。

        运行这个:

        >>> g=sieve()
        

        然后每个后续调用都返回下一个素数:

        >>> next(g)
        2
        >>> next(g)
        3
        # etc
        

        然后,您可以使用 islice 获取边界之间的列表(即,从第一个到 X+Y 素数的第 X 个素数...):

        >>> tgt=0
        >>> tgt, list(it.islice(sieve(), tgt, tgt+10))
        (0, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29])
        >>> tgt=1000000
        >>> tgt, list(it.islice(sieve(), tgt, tgt+10))
        (1000000, [15485867, 15485917, 15485927, 15485933, 15485941, 15485959, 15485989, 15485993, 15486013, 15486041])
        

        【讨论】:

          【解决方案6】:

          获取第 100 个素数:

          import itertools
          n=100
          x = (i for i in itertools.count(1) if all([i%d for d in xrange(2,i)]))
          print list(itertools.islice(x,n-1,n))[0]
          

          要得到直到 100 的素数

          import itertools
          n=100
          x = (i for i in xrange(1,n) if all([i%d for d in xrange(2,i)]))
          for n in x:
              print n
          

          【讨论】:

            【解决方案7】:

            你从这里开始:

            def prime_gen(n):
                primes = [2]
                a = 2 
            
               while a < n:
                   counter = 0 
            
                   for i in primes:
                      if a % i == 0:
                          counter += 1
            
                    if counter == 0:
                        primes.append(a)
                    else:
                        counter = 0
            
                    a = a + 1
            
                print primes
            

            你真的需要 else 分支吗?没有。

            def prime_gen(n):
                primes = [2]
                a = 2 
            
               while a < n:
                   counter = 0 
            
                   for i in primes:
                      if a % i == 0:
                          counter += 1
            
                    if counter == 0:
                        primes.append(a)
            
                    a = a + 1
            
                print primes
            

            你需要柜台吗?不!

            def prime_gen(n):
                primes = [2]
                a = 2 
            
               while a < n:
            
                   for i in primes:
                       if a % i == 0:
                           primes.append(a)
                           break 
            
                    a = a + 1
            
                print primes
            

            你需要检查 i 是否比 sqrt(a) 大吗?没有。

            def prime_gen(n):
                primes = [2]
                a = 3 
            
               while a < n:
                   sqrta = sqrt(a+1)
                   for i in primes:
                       if i >= sqrta:
                           break
                       if a % i == 0:
                           primes.append(a)
                           break 
            
                    a = a + 1
            
                print primes
            

            你真的要手动增加一个吗?

            def prime_gen(n):
                primes = [2]
            
               for a in range(3,n):
                   sqrta = sqrt(a+1)
                   for i in primes:
                       if i >= sqrta:
                           break
                       if a % i == 0:
                           primes.append(a)
                           break 
            

            这是一些基本的重构,应该会自动从您的手指中流露出来。

            然后你测试重构的代码,看看它有问题并修复它:

            def prime_gen(n):
                primes = [2]
                for a in range(3,n):
                    sqrta = sqrt(a+1)
                    isPrime = True 
                    for i in primes:
                        if i >= sqrta:
                            break
                        if a % i == 0:
                            isPrime = False
                            break 
                    if(isPrime): 
                        primes.append(a)
                return primes
            

            最后你摆脱了 isPrime 标志:

            def prime_gen(n):
                primes = [2]
                for a in range(3,n):
                    sqrta = sqrt(a+1)
                    for i in primes:
                        if i >= sqrta:
                            primes.append(a)
                            break
                        if a % i == 0:
                            break 
                return primes
            

            现在你相信你已经完成了。然后突然你的一个朋友指出,对于a,即使你无缘无故地检查i &gt;= sqrta。 (对于a mod 3 == 0 数字也是如此,但随后分支预测会有所帮助。) 你的朋友建议你之前检查a % i == 0

            def prime_gen(n):
                primes = [2]
                for a in range(3,n):
                    sqrta = sqrt(a+1)
                    for i in primes:
                        if a % i == 0:
                            break 
                        if i >= sqrta:
                            primes.append(a)
                            break
                return primes
            

            现在你已经完成了,感谢你聪明的朋友!

            【讨论】:

            • 还有其他方法可以计算前 n 个素数。我试图展示如何重构你的代码。请注意,对于 Python 2.x,xrange 比 xrange 快,但我假设您使用的是 3.x
            • 在我的机器上,当 n = 100 时,我的速度是原始版本的两倍。当 n = 1000 时,我的速度快 10 倍。对于 n = 10'000,我的版本快 60 倍。对于 n = 100'000,它快 325 倍!
            • 如果在 i &gt;= sqrta 之前检查 a % i == 0 ,代码运行得更快。至少在我的笔记本电脑上,对于 n = 1,000,000,代码所花费的时间始终减少了 5%。
            • @gourevkr 感谢您的检查。我正在考虑重构代码。
            • 我是来上学的 :-D 顺便说一句,在我的硬件上,我们得到的程序比公认的答案快 7 倍!
            【解决方案8】:

            你也可以这样做来获取python字典中的素数

            def is_prime(a):
                count = 0
                counts = 0
                k = dict()
                for i in range(2, a - 1):
                    k[count] = a % i
                    count += 1
                for j in range(len(k)):
                    if k[j] == 0:
                        counts += 1
            
                if counts == 0:
                    return True
                else:
                    return False
            
            
            def find_prime(f, g):
                prime = dict()
                count = 0
                for i in range(int(f), int(g)):
                    if is_prime(i) is True:
                        prime[count] = i
                        count += 1
                return prime
            
            a = find_prime(20,110)
            print(a)
            
            {0: 23, 1: 29, 2: 31, 3: 37, 4: 41, 5: 43, 6: 47, 7: 53, 8: 59, 9: 61, 10: 67, 11: 
            71, 12: 73, 13: 79, 14: 83, 15: 89, 16: 97, 17: 101, 18: 103, 19: 107, 20: 109}
            

            【讨论】:

              【解决方案9】:

              您可以使用 Python 的 yield 语句一次生成一项。儿子不是一次获取所有项目,而是迭代生成器并一次获取一项。这样可以最大限度地减少您的资源。

              这里是一个例子:

              from math import sqrt
              from typing import Generator
              
              
              def gen(num: int) -> Generator[int, None, None]:
                  if 2 <= num:
                      yield 2
                  yield from (
                      i
                      for i in range(3, num + 1, 2)
                      if all(i % x != 0 for x in range(3, int(sqrt(i) + 1)))
                  )
              
              
              for x in gen(100):
                  print(x, end=", ")
              

              输出:

               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, 
              

              【讨论】:

                【解决方案10】:

                我对我的jimifiki提出的解决方案进行了改进

                import math #for finding the sqare root of the candidate number
                def primes(n):
                  test = [3] #list of primes new candidates are tested against
                  found = [5] #list of found primes, which are not being tested against
                  c = 5 #candidate number, starting at five
                  while c < n: #upper bound, largest candidate will be this or 1 bigger
                    p = True #notes the possibility of c to be prime
                    c += 2 #increase candidate by 2, avoiding all even numbers
                    for a in test: #for each item in test
                        if c % a == 0: #check if candidate is divisible
                            p = False #since divisible cannot be prime
                            break #since divisible no need to continue checking
                    if p: #true only if not divisible
                        if found[0] > math.sqrt(c): #is samallest in found > sqrt of c
                            found.append(c) #if so c is a prime, add it to the list
                        else: #if not, it's equal and we need to start checking for it
                            test.append(found.pop(0)) #move pos 0 of found to last in test
                  return([2] + test + found) #after reaching limit returns 2 and both lists
                

                最大的改进是不检查偶数,只检查不可整除的平方根,当数字变大时,平方根才真正加起来。我们不需要检查平方根的原因是,测试列表只包含小于平方根的数字。这是因为只有当我们得到第一个不能被测试中的任何数字整除的非素数时,我们才会添加下一个数字。这个数总是下一个最大素数的平方,它也是找到的最小数。布尔值“p”的使用对我来说有点像意大利面,所以可能还有改进的余地。

                【讨论】:

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