1.求素数用到的方法
(1)普通枚举 枚举到sqrt(n)就好
(2)朴素筛法
通过对每个数有无非平凡因子来判断
朴素筛法 nlogn ,这个时间复杂度的计算有些复杂
如下
n/2+n/3+n/4+…+n/n;
n(1/2+1/3+1/4+…+1/n)
后面那一坨大致就约等于 ln(n) (学过数竞应该知道)
int a[maxn],b[maxn],n;
int cnt=0;
//筛出 2 到 n 中的所有素数
void naive_sieve(int n){
for(int i=2;i<=n;i++)
{
if(!a[i]) b[cnt++]=i;
for(int j=2*i;j<=n;j+=i)//从i+i往后筛所有的倍数
a[j]=1;
}
}
(3)埃式筛法
通过素因子来筛
int a[maxn],b[maxn],n;
int cnt=0;
//优化, nloglogn ,这里的计算跟上个差不多,埃式筛法
void naive_sieve(int n){
for(int i=2;i<=n;i++)
{
if(!a[i])
{
b[cnt++]=i;
for(int j=2*i;j<=n;j+=i)//将此循环体拿到if依据里面
//意思就是,在遍历筛的时候遇到的如果是合数就不用筛了,优化
a[j]=1;
}
}
}
(4)线性筛(欧拉筛)
void oula_sieve(int n)
{
int cnt=0;
for(int i=2;i<=n;i++)
{
if(!a[i]) b[cnt++]=a[i];
for(int j=0;b[j]*i<=n;j++)
{
a[b[j]*i]=1;
// 保证b[j]是i*b[j]1的最小素因子
if(i%b[j]==0) break;
}
}
}
大家可能对以上三种方法及其优化思路还有疑问,下面加深讲解
1.基本思想其实都是先筛掉有非平凡因数的合数筛掉,再进行存储
2.举下面 一个栗子
比如说我们要筛 12这个数
(1)朴素筛 : 除1外每个数都要去把12筛一遍
也就是我们扫到这些数都要去把12筛 : 2 3 4 6
(2)埃氏筛 : 除1外每个质因数都要去把12筛一遍
也就是我们扫到这些数都要去把12筛 : 2 3
(3)欧拉筛 :
可能有人不太懂
1–j代表的是下标
2–b[]存储找到的素数
代码中的第二重(if依据巧妙避开求重)可以让标记只让当前找到的素因数往后标记
3–还是举12的例子
12的最小质因数会将12筛出
也就是只有:2
4–总结欧拉筛:
每次用已筛出来的质数去筛更大的数,
每个合数只被它最小的质因子筛掉,
试想,如果26筛了12之后还没break,
而是用36筛掉18,那么18还会被29筛一次,就重复了
而根本原因就是6有2这个因子,
而36筛掉的数一定也有2这个因子,
3*6这个数应该被2这个因子筛掉,而不是3