記錄一段生成素數python程式碼的調優過程 • cenalulu's Tech Blog
簡介:本文主要記錄了博主對一段使用python實現的素數生成程式碼的不斷優化過程。
背景:最近在刷Project Euler的題目,刷到第十題(計算2百萬以下素數的和)的時候發現之前的素數生成程式碼效率太低導致幾分鐘都出不來。於是通過不斷的調優,終於得到一個能在秒級算出2百萬以內的素數的generator。 本文的調優過程基本不涉及基於數論的調優,如果您希望得到一個擁有極致效能的python素數生成程式碼,可以使用pyprimes
第一版
素數也即:無法被除了1和本身以外的任何自然數整除的自然數。因此第一版的程式實現起來也格外的直白。把從2
開始到數值本身-1
範圍內的自然數都去除一下待判斷的數就能得到結論了。於是就有了第一版程式如下:
class Prime:
def __init__(self):
self.prime_list = []
self.v = 3
def get_prime(self):
yield 2
while True:
for i in range(2, self.v):
if self.v % i == 0:
break
else:
self.prime_list.append(self .v)
yield self.v
self.v += 1
第二版
當然這段程式碼對於小的素數是可以work的。單當使用這段程式碼除錯Euler第三題的時候就會發現在判斷大數是否是素數時耗時很久。判斷一個數是否是素數的時間複雜度目前是O(N^2)
。於是就想到需要通過減少判斷次數來達到提升程式碼效率的方法。最容易想到的一種方法是:大於被判斷值的平方根的數,無需再去判斷是否可以整除被判斷數了。現在單個素數的判斷時間複雜度優化到O(logN)
class Prime:
def __init__(self):
self .prime_list = [2]
self.v = 3
def get_prime(self):
yield 2
while True:
for i in (3, self.v):
if self.v % i == 0:
break
elif i * i > self.v + 1:
yield self.v
self.prime_list.append(self.v)
break
else:
self.prime_list.append(self.v)
yield self.v
self.v += 1
第三版
當然這還遠遠不夠,接踵而來的是第7題(找到第1000個素數)。尋找第N個素數的命題大大放大了我們時間複雜度。目前程式的時間複雜度是O(NlogN)
。於是再次尋找是否還有其他可以跳過的檢查項。我們可以發現所有偶數其實是可以完全可以跳過不判斷的。但是判斷偶數本身也是一次計算操作,所以簡單的通過if self.v % 2 == 0
來跳過一次檢查並不能帶來很大的效能提升,所以這裡用了一個比較tricky方式跳過偶數:由於序列是從3開始檢查,因此把增長步進調整為2,那麼就自然跳過了所有偶數
class Prime:
def __init__(self):
self.prime_list = []
self.v = 3
def get_prime(self):
yield 2
while True:
for i in self.prime_list:
if self.v % i == 0:
break
else:
self.prime_list.append(self.v)
yield self.v
self.v += 2
這部分優化後,找到第N個素數的時間比原來少了一半。
第四版(最終版)
實際執行後發現即使時間相較之前少了一半,但總體執行時間仍然不理想(十幾秒級別)。冥想。。。過後又有一個大招:由於所有合數都能寫成素數相乘,因此如果能夠被某個合數整除,也必然能被某個素數整除。那麼我們的所有除數只需從被判斷的數小的素數集合中選擇即可。於是得到以下優化程式碼
class Prime:
def __init__(self):
self.prime_list = [2]
self.v = 3
def get_prime(self):
yield 2
while True:
for i in self.prime_list:
if self.v % i == 0:
break
elif i * i > self.v + 1:
yield self.v
self.prime_list.append(self.v)
break
else:
self.prime_list.append(self.v)
yield self.v
self.v += 2
這部分優化後,找到第N個素數的時間從O(N/2logN)
變成了O((logN)^2)