Question:找出100以内的所有质数
这是一个很基础很经典的小代码,很大概率我们会这么去遍历筛选:
def find_odd(numb):
flag = 0
if numb ==2:
return True
else:
for i in range(2,numb):
flag = 0
if numb%i == 0:
return False
flag = 1
if flag == 1:
return True
if __name__ == "__main__":
my_list = []
for i in range(2,101):
if find_odd(i) ==True:
my_list.append(i)
else:
continue
print(my_list)
Fine, 它很正常的完成了我们的既定任务,但是现在要求速度更快一点呢?如果不是100以内而是更大范围内的数呢?
这里我们只需要一点点小小的改动,将遍历从2到改为从2到
这样处理其大范围时候,会省去很多很多时间。
def find_odd(numb):
flag = 0
if numb ==2:
return True
else:
j = int(round(numb**0.5)+1)
for i in range(2,j):
flag = 0
if numb%i == 0:
return False
flag = 1
if flag == 1:
return True
if __name__ == "__main__":
my_list = []
for i in range(2,101):
if find_odd(i) ==True:
my_list.append(i)
else:
continue
print(my_list)
那么再快一点呢?
好吧这里要用到一个结论:大于等于5的任何质数都必定分布在六的倍数附近
注意该结论的逆命题非True
简单可以这么理解:2*2=4,2*3=6,2*2*2=8,2和3的整倍数必定不是质数,他们俩的整倍数铺满的整个整数轴上,只有2*3的倍数附近可能有空隙
也就是说,我们不必筛选从1到numb这么多个数一次一次去判断,只需要在六的倍数附近进行判断即可
def find_odd(numb):
flag = 0
if numb ==2:
return True
else:
j = int(round(numb**0.5)+1)
for i in range(2,j):
flag = 0
if numb%i == 0:
return False
flag = 1
if flag == 1:
return True
if __name__ == "__main__":
my_list = [2,3]
for i in range(1,100//6+1):
if find_odd(i*6-1) ==True:
my_list.append(i*6-1)
if find_odd(i*6+1)==True:
my_list.append(i*6+1)
print(my_list)
再这两个优化下,正常代码已经是很快了,但是如果要更快呢???
这里就要引入大名鼎鼎的算法欧拉筛
def EulerSieve(n):
filter, primers = [False for _ in range(n + 1)], []
for i in range(2, n + 1):
if not filter[i]:
primers.append(i)
for prime in primers:
if i * prime > n:
break
filter[i * prime] = True
if i % prime == 0:
break
return primers
if __name__ =="__main__":
mylist = EulerSieve(100)
print(mylist)
先将filter全部赋值为False作为我们的“筛子”,再生成一个空列表primers储存结果
判断的逻辑为,如果filter[i]这个元素为False那么就添加该i这个元素(即为质数)到primers里
然后再在primers里对每个元素乘当前循环量i,把filter中对应乘后索引值改为True(即被筛掉了),因为质数的整倍数必定为非质数。这样我们就又减小了很多运算量。
那么我们上代码检验一下是不是真的会快很多呢?
import timeit
import matplotlib.pyplot as plt
def compare():
E_time=[]
N_time=[]
Step=[]
for step in range(1000,20001,2000):
def EulerSieve(n=step):
filter, primers = [False for _ in range(n + 1)], []
for i in range(2, n + 1):
if not filter[i]:
primers.append(i)
for prime in primers:
if i * prime > n:
break
filter[i * prime] = True
if i % prime == 0:
break
return primers
def find_odd(numb):
flag = 0
if numb ==2:
return True
else:
j = int(round(numb**0.5)+1)
for i in range(2,j):
flag = 0
if numb%i == 0:
return False
flag = 1
if flag == 1:
return True
def Normal_find(n=step):
mylist = []
for i in range(2,n+1):
if find_odd(i) == True:
mylist.append(i)
return mylist
E_time.append(round(timeit.timeit(EulerSieve, number=100),4))
N_time.append(round(timeit.timeit(Normal_find, number=100),4))
Step.append(step)
return E_time,N_time,Step
if __name__ =="__main__":
Y1_list,Y2_list,X_list=compare()
plt.plot(X_list,Y1_list,color = 'red',label='EulerSieve Method')
plt.plot(X_list,Y2_list,color='green',linewidth=1.0,linestyle='--',label='Normal Method')
plt.ylabel('耗时/s',fontproperties = 'SimHei',fontsize = 12)
plt.xlabel('最大质数范围/百次',fontproperties = 'SimHei',fontsize = 12)
plt.title('欧拉筛和常规算法筛选质数时间比较',fontproperties = 'SimHei',fontsize = 15)
plt.show()
直接放结果了
绿色虚线为正常算法,红色实线为欧拉筛算法,肉眼可见的优秀