根号筛
根据素数定义,一个数是素数当且仅当它只能被(1)和它本身整除
根据定义我们可以枚举(2—n-1)中的每一个数判断是否能整除(n)
根据什么不知名的显然定理,若(a*b=n),且(a≤b),那么必定有(a≤sqrt{n},b≥sqrt{n})
所以只需要枚举(2——sqrt{n})的数字判断(n)是否为质数
埃氏筛
根号筛每筛一个数就要(sqrt{n})复杂度,要求(1—n)内的质数就要(O(nsqrt{n}))
介绍更快捷的埃氏筛
原理很简单,将每个质数的倍数全部筛掉
近似复杂度分析:
根据素数分布定理,(n)以内的素数约为(frac{n}{ln n})个,第(i)个素数约为(iln i(计ln 1=1))
算法复杂度:
积分估值
欧拉筛
为什么埃氏筛会有额外的复杂度?因为每个素数在筛自己的倍数的时候可能会存在几个素数筛同一个合数的情况
例如:(15=3*5)
那么按照埃氏筛的方法,(15)会被(3)筛一次,又会被(5)筛一次
如果我们规定一个方式,比如每个数只由它最小的质因数筛掉,那么复杂度理所当然会优化到(O(n))
考虑以下如何让每个数字被最小的质因数筛掉
for(int i=2;i<=n;++i)
{
if(!vis[i]) prime[++num]=i;
for(int j=1;i*prime[j]<=n;++j)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
首先由于我们要用最小质因数来筛合数,我们把确定质因数枚举乘数变成确定乘数枚举质因数
因为在确定质因数枚举乘数时,乘数的最小质因数是不连续的,难以实现用最小质因数筛合数
而在确定乘数枚举质因数时,乘数有固定的最小质因数,往下看可以发现很方便实现
如果一个数字(i)在前几轮都没有被筛掉,那么说明它是一个质数
接下来用(i*prime[j])来筛掉合数,到(i%priem[j]==0)时退出,分析为什么这样可以做到每个数字被最小质因数筛掉
设(s<j,k>j)
1.(i)的最小质因数是(prime[j]),否则在枚举到(prime[s])时就会结束循环
2.(i*prime[s])的最小质因数是(prime[s]),如果(i*prime[s])的最小质因数是更小的(prime[t],t<s),(prime[s])是质数,那么只能(i|prime[t]),在之前就会跳出
3.(i*prime[k])的最小质因数是(prime[j]),因为(i|prime[j])
根据(3),如果(i|prime[j])时不退出循环,那么我们就会用(prime[k])去筛(i*prime[j]),不符合用最小质因数筛合数的要求
所有合数都会被标记证明:
设一任意合数为(A),其最小质因数为(p),令(A=B*p),那么(B)的最小质因数不会小于(p),否则(p)将不会是(A)的最小质因数,在外层枚举乘数到(B)时,因为(B)的最小质因数不小于(p),所以一定会被筛掉
不会被非最小质因数筛到证明:
比如(105=3*5*7),在(3)处筛了一次,当枚举乘数为(21)时,在枚举到质因数(5)之前会被质因数(3)给(pass)掉