欧拉筛和常规算法筛选质数时间比较

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到numb改为从2到\sqrt(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 = []
    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()

直接放结果了

绿色虚线为正常算法,红色实线为欧拉筛算法,肉眼可见的优秀

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值