欧拉筛(或者说是线性筛)的核心详解

首先欧拉筛的代码

n=16
data=[i for i in range(0,n)]
flag=[1]*n
single=[]
for i in range(2,n):
    if flag[i]==1:
        single.append(data[i])
    for ii in range(len(single)):
        if single[ii]*i<n:
            flag[single[ii]*i]=0
        if i%single[ii]==0:
            break
print(data)
print(flag)
print(single)

假如去掉了if i%single[ii]==0:break

n=16
data=[i for i in range(0,n)]
flag=[1]*n
single=[]
for i in range(2,n):
    if flag[i]==1:
        single.append(data[i])
    for ii in range(len(single)):
        if single[ii]*i<n:
            flag[single[ii]*i]=0
print(data)
print(flag)
print(single)

这个就变成了一个优化的埃氏筛:跟以下的优化方法一样

  • (见以下m=x这一条,保证了筛的和数时候一定是一个质数和比它大的数的积,避免了使得只会出现12=3X4,12=2X6筛两遍,去掉(12=4X3,12=6X2)的两遍。)
  • 当然首先大家得承认一个合数一定能拆成一个小的质数和另一个较大的数,所以才能用一个比质数相等或更大的数乘质数
n=10
a=[i for i in range(2,n)]
for x in range(2,int(n**(0.5)+1)):
    m=x
    while m*x<=(n-1):
        try:
            a.remove(m*x)
        except:
            pass
        m+=1
print(a)

开始前我们首先要承认:

  • 每一个合数是可以完全拆成质数的乘积如12=2X2X3
  • 每个合数都有且只有一个最小的质数(可重复)

以下需要理解我说的每一行:

加上if i%single[ii]==0:break
欧拉筛的思想是只让合数去最小质数那里报到。
①如何让合数只在最小的质数那里报到呢,
那就是让合数只能在最小质数和某个数乘积处被标记,在比最小质数大的质数处跳过;
对于数i,
我们从小到大遍历质数列表single,
将质数跟i相乘得到合数,
对于任意的i,此时比质数列表的任意数大,
当i不能被质数列表从小到大检索的较小的质数整除时,该较小的质数便是该质数乘i得到的合数的最小质数。
当遍历到较大的质数时,发现i能整除single[ii]这个数,注意啊,此时该较大质数还是该质数乘i得到的合数的最小质数,只是该合数中的该最小质数出现了多个。
所以该合数还是出自最小质数,
因此if i%single[ii]==0:break放在该情况的合数也被标记完才跳,
对于x>ii时的所有质数和i的积single[x]*i,因为这些往后的所有得到的的积本身的最小质数是single[ii],因为single[x]>single[ii]且i的最小质数是single[ii],得出来的合数不符合①,所以直接跳出。要问这些积在什么时候能被标记,答:也在积的最小质数single[ii]处被标记,那的等到数new_i=single[x]*old_i/single[ii],我相信这个new_i是指新的数,那个old_i就是指现在这个i的值,等到时候就是新的i乘最小质数也就是
(single[x]*old_i/single[ii])* single[ii] = single[x]*old_i。

OVER.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值