zoukankan      html  css  js  c++  java
  • 埃拉托色尼素数筛选法的证明及原理

    一、什么是素数?

      素数又称为质数。素数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。素数在日常中最多的应用就是加密算法,例如RSA加密算法就是基于来实现的。RSA算法会随机生成两个1024位的质数相乘,要破解密码必须对乘积做质因数分解,而1024位的质因数分解是非常困难的。

    二、如何快速的算出小于某个数的所有素数?

      从素数的概念上似乎可以很快得出一个算素数的公式,即将一个数循环做除法,从1一直除到它本身,如果中途只有被1和自己整除了这个数便是素数了,这样的计算效率是非常差的,因为他会不停的遍历整个数据范围。我们来试着跑一下它的效率:

    #求10万以内的素数
    import datetime
    start = datetime.datetime.now()
    for i in range(2,100000):
        for j in range(2,i):
            if i%j == 0:
                break
        #else:
            #print(i)
    stop = datetime.datetime.now()
    print(stop-start)

      运行结果: 0:01:04.326679 ,在没有print的情况下竟然用了1分多钟,并且数字越大计算越慢。这样的效率肯定是不被允许的。下面介绍一种最常见的质数算法的原理:

    三、埃拉托色尼素数筛选法

      埃拉托色尼是一名古希腊的地理学家,他是世界上第一个计算出地球周长的人。埃拉托色尼素数筛选法可以很快速的计算出1到N之间的所有素数。将n开根号,即N^0.5,去掉2到N^0.5中所有素数的倍数,剩下的数便都是素数了。例如求1到25中的素数有哪些,第一步是将25开根号,得到5;第二步将2到5的素数取出来,分别是2、3、5;再将2到25中且是2、3、5的倍数的数去掉,即去掉4、6、8、9、10、12、14、15、16、18、20、21、22、24、25,剩下2、3、5、7、11、13、17、19便是1到25中的所有素数了。

      这里还用到了一个数学知识,即只要小于或等于根号N的数(1除外)不能整除N,则N一定是素数;反过来说就是只要小于或等于根号N的数(1除外)能整除N,则N一定是合数。下面给出证明过程:

    假设一个数N是合适,那一定存在一个因数b和一个非1且非自身的因数a,即a*b=N

    等式两边同时开根号得出:(a*b)^0.5=a^0.5*b^0.5=N^0.5

    可以推出:若N为合数,则a和b中一定有一个大于或等于N^0.5,另一个小于或等于N^0.5

      按照这个结论,就只需计算小于等于N^0.5的数了,这样就大大提高了效率(要注意等于N^0.5的这个边界):

    #求10万以内的素数
    import datetime
    start = datetime.datetime.now()
    for i in range(2,100000):
        for j in range(2,int(i**0.5+1)): #边界需要加1
            if i%j == 0:
                break
        #else:
            #print(i)
    stop = datetime.datetime.now()
    print(stop-start)

      运行结果: 0:00:00.428024 。可以说是质的飞跃。

    再优化

      我们也可以将偶数事先就排除在外,然后在进行素数筛选:

    #求10万以内的素数
    import datetime
    start = datetime.datetime.now()
    print(2)
    for i in range(3,100000,2): #从3开始,跳过偶数
        for j in range(2,int(i**0.5+1)):
            if i%j == 0:
                break
        #else:
            #print(i)
    stop = datetime.datetime.now()
    print(stop-start)

      运行结果: 0:00:00.406024 ,又快了不少。

     再次优化:

      可以思考:在第一个for循环中已经将跳过了所有的偶数,在 if i%j == 0: 语句中i只有奇数,而在数学中任何奇数除以偶数是一定除不尽的,也就数说当j为偶数时,语句 i%j == 0: 一定是为假的。所以可以将j中的偶数也去掉

    #求10万以内的素数
    import datetime
    start = datetime.datetime.now()
    print(2)
    for i in range(3,100000,2): #从3开始,跳过偶数
        for j in range(3,int(i**0.5+1),2): #再将j的偶数去掉
            if i%j == 0:
                break
        #else:
            #print(i)
    stop = datetime.datetime.now()
    print(stop-start)

      运行结果: 0:00:00.271016 ,比之前的有快了20毫秒。

    再三优化:

      用列表的方式进行算法再优化。思路是将每次i循环后如果得到的i是素数则将它累加到一个列表中,并使j在for循环中以这个列表为可迭代对象进行迭代循环,因为列表中所有的元素都是素数,所以j如果迭代到小于等于i^0.5次方时没有能整除的数,说明大于i^0.5次方时也没有能整除的,即此时的i就是素数。这里也用到了一个数学原理:一个数如果能被小于它的素数整除则它一定是一个合数,反之它一定是一个素数。

    import datetime
    start = datetime.datetime.now()
    n = 100000
    lst = []
    flag = True
    #print(2)
    for i in range(3,n,2):
        up = i**0.5
        for j in lst:
            if j > up:
                flag = True
                break
            if i % j ==0:
                flag = False
                break
        if flag:
            #print(i)
            lst.append(i)
    stop = datetime.datetime.now()
    print(stop-start)

    运行结果: 0:00:00.177010 ,又快了!

    其它优化方案:

      优化方向:

      1.孪生素数对猜想:大于等于5的素数一定与6的倍数相邻。

      2.在奇数中在排除5的倍数。

  • 相关阅读:
    Linux学习之查看是否安装软件
    Linux学习之nfs实例
    Linux学习之nfs安装配置
    Linux 学习之防火墙配置
    Linux学习之系统时间同步
    Linux学习之守护进程详解
    Linux学习之Center os网络配置
    Linux学习之挂载操作
    Linux学习之挂载
    Linux学习之开机启动
  • 原文地址:https://www.cnblogs.com/readygood/p/10202004.html
Copyright © 2011-2022 走看看