埃拉托色尼素數篩選法的證明及原理


一、什么是素數?

  素數又稱為質數。素數定義為在大於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的倍數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM