美文网首页
关于求N以内素数的python实现以及优化方法

关于求N以内素数的python实现以及优化方法

作者: vampire6 | 来源:发表于2018-08-04 11:12 被阅读0次

    一、素数的定义

    ​ 质数(prime number)又称素数,有无限个。除了1和它本身以外不再有其他的除数整除。从定义知道;1不是素数,最小的素数是2。

    二、N以内素数常用实现方法

    ​ 首先教科书写法(暂时不做任何代码优化):

    import math
    def prime(n):
        if n <= 1:
            return 0
        #for i in range(2,int(math.sqrt(n)+1)):
        for i in range(2,n):
            if n%i == 0:
                return 0
        return 1
    if __name__ == "__main__":
        n = int(input(">>"))
        for i in range(2,n+1):
            if prime(i):
                print (i)
    

    ​ 代码中注释行是取了[2,√n+1]作为除数范围,通过对比测试,显然,[2,√n+1]范围下,效率快了很多。

    三、优化方法

    原理层面

    ​ 1、除了2以外,其余的偶数显然不可能是素数,再来看奇数,1不是素数,从3开始看,除了3以外,其余能被3整除的都是合数,再看5,除了5以外,其余能被5整除的都是合数,加起来,一共在[2,√n+1]范围内排除了近3/4的计算量。

    ​ 2、另外使用埃拉托斯特尼筛法(希腊语:κόσκινον Ἐρατοσθένους,英语:sieve of Eratosthenes ),简称埃氏筛,也有人称素数筛。这是一种简单且历史悠久的筛法,用来找出一定范围内所有的素数。

    所使用的原理是从2开始,将每个素数的各个倍数,标记成合数。一个素数的各个倍数,是一个差为此素数本身的等差数列。

    算式:
    给出要筛数值的范围n,找出\sqrt{n}以内的素数 p1,p2,...,pk
    先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个素数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个素数5筛,把5留下,把5的倍数剔除掉;不断重复下去......。

    Eratosthenes原理
    def eratosthenes(n):
        IsPrime = [True] * (n + 1)
        IsPrime[1] = False
        for i in range(2, int(n ** 0.5) + 1):
            if IsPrime[i]:
                for j in range(i * 2, n + 1, i):
                    IsPrime[j] = False
        return {x for x in range(2, n + 1) if IsPrime[x]}
    
    if __name__ == "__main__":
        print (eratosthenes(n))
    

    代码层面

    第一种优化思路:

    
    import math
    def prime(n):
        if n%2 == 0:
            return n==2
        if n%3 == 0:
            return n==3
        if n%5 == 0:
            return n==5
        for p in range(7,int(math.sqrt(n))+1,2):    #只考虑奇数作为可能因子
            if n%p == 0:
                return 0
        return 1
    
    if __name__ == "__main__":
        n = int(input(">>"))
        for i in range(2,n+1): #1不是素数,从2开始
            if prime(i):
                print i
    

    ​ 再来实现第二种思路,代码如下:

    #寻找n以内的素数,看执行时间,例子100000内的素数
    
    def prime(n):
        flag = [1]*(n+2)
        p=2
        while(p<=n):
            print p
            for i in range(2*p,n+1,p):
                flag[i] = 0
            while 1:
                p += 1
                if(flag[p]==1):
                    break
    
    # test
    if __name__ == "__main__":
        n = int(input(">>"))
        prime(n)
    

    统一测试下差异很清楚。第二种方法要优于第一种,再优化下代码
    首先,将range换成xrange,再测试下:两种方法速度都有提升。range和xrange的差异,range是一次性连续返回一个列表,而xrange是每次只生成一个,并且不保留上次生成的值。

    致命错误:
    对于range(2*p,n+1,p),还有一种实现方法,range(2*p,n+1)[::p],但这两种写法,完全不相干,range(2*p,n+1,p)返回的列表就是按照p步长来生成的,而range(2*p,n+1)[::p],是生成了步长为1的列表,最后列表执行切片操作,只取p步长的值返回,显然没有range(2*p,n+1,p)的实现更为直接,两者虽然返回值一样,但经过实际测试发现,效率差异非常大,甚至可以颠覆算法的优势。

    ​在这几种方案中,最后一种速度最快,效率最高,但有个应用前提,就是待搜索列表必须是有序且连续的,所以比较适合N以内符合某条件的数字。

    相关文章

      网友评论

          本文标题:关于求N以内素数的python实现以及优化方法

          本文链接:https://www.haomeiwen.com/subject/jmopvftx.html