//加了下面的代码,博客会禁止复制(代码还可以复制) // document.body.onselectstart = document.body.ondrag = function(){return false;}

素数筛和质因数分解

素数筛

常用的素数筛有埃式筛和欧拉筛,这里介绍性能更优的欧拉筛

欧拉筛

欧拉筛是一种线性筛,它能在\(O(n)\)的线性时间内求得\(1\sim n\)内所有素数。欧拉筛是对埃式筛的改进。
具体操作步骤如下。
(1)逐一检查\(2\sim n\)的所有数。第一个检查的是2,它是第1个素数。
(2)当检查到第i个数时,利用已经求得的素数去筛掉对应的合数x,而且是用x的最小质因数去筛。

代码

int prime[N];//保存素数
bool vis[N];//记录是否被筛
int euler_sieve(int n){//欧拉筛,返回素数的个数
    int cnt = 0;//记录素数个数
    memset(vis,0,sizeof(vis));
    memset(prime,0,sizeof(prime));
    for(int i = 2;i<=n;++i){//检查每个数,筛去其中的合数
        if(!vis[i])
            prime[cnt++]=i;//如果没有筛过,是素数,记录;第一个素数为2
        for(int j = 0;j<cnt;++j){//用已经得到的素数去筛后面的数
            if(i*prime[j]>n) break;//只筛小于或等于n的数
            vis[i*prime[j]]=1;//用x的最小质因数筛去x
            if(i%prime[j]==0) break;//如果不是这个数的最小质因数,则结束
        }
    }
    return cnt;//返回小于或等于n的素数个数
}

质因数分解

用欧拉筛求最小质因数

在介绍常见质因数分解算法之前,先介绍欧拉筛的一个应用:求\(1\sim n\)内每个数的最小质因数。只要简单修改欧拉筛的代码,直接用vis[N]记录最小质因数就可以。”欧拉筛求素数+最小质因数“代码如下。

int prime[N];//保存素数
bool vis[N];
int euler_sieve(int n){//欧拉筛,返回素数的个数
    int cnt = 0;//记录素数个数
    memset(vis,0,sizeof(vis));
    memset(prime,0,sizeof(prime));
    for(int i = 2;i<=n;++i){//检查每个数,筛去其中的合数
        if(!vis[i]){
            vis[i] = i;
            prime[cnt++]=i;
            }//vis[]记录最小质因数
        for(int j = 0;j<cnt;++j){//用已经得到的素数去筛后面的数
            if(i*prime[j]>n) break;//只筛小于或等于n的数
            vis[i*prime[j]]=prime[j];//vis[]记录最小质因数
            if(i%prime[j]==0) break;//如果不是这个数的最小质因数,则结束
        }
    }
    return cnt;//返回小于或等于n的素数个数
}

用试除法分解质因数

步骤如下:
(1)求最小质因数\(p_1\)。逐个检查\(2\sim \sqrt{n}\)的所有素数,如果它能整除n,就是最小质因数。然后连续用\(p_1\)除n,目的是去掉n中的\(p_1\),得到\(n_1\)
(2)再找n1的最小质因数。逐个检查\(p_1\sim \sqrt{n_1}\)的所有素数。从\(p_1\)开始试除,是因为\(n_1\)没有比\(p_1\)小的质因数,而且\(n_1\)的因数也是n的因数。
(3)继续以上步骤,直到找到所有质因数。
最后,经过去除因数的操作后,如果剩下一个大于一的数,那么它也是一个素数,是n的最大质因数。
试除法的复杂度为\(O(\sqrt{n})\),效率很低,但在算法竞赛中,数据规模不大,所以一般就用试除法。
下面给出试除法的代码。因为试除法的效率不高,所以n用int型,没有用long long 型

int p[20];//p[]记录因数
int c[40];//c[i]记录第i个因数的个数,一个因数的个数最多有三十几个。
int factor(int n){
    int m = 0;
    for(int i = 2; i*i <= n; ++i){
        if(n%i == 0){
            p[++m] = i, c[m] = 0;
            while(n % i == 0)
                n/=i, c[m++];//把n中重复的因数去掉
        }
    }
    if(n > 1) p[++m] = n, c[m] = 1;//没有被除尽,是素数
    return m;//共m个
}
posted @ 2023-04-12 21:32  龙鳞墨客  阅读(81)  评论(0)    收藏  举报