线性筛素数与欧拉函数

线性筛素数

问题:给出n,输出所有<=n的素数。

1.暴力做法:

枚举一个 i (3<=i<=n) 暴力判断 i 是否是素数,是的话输出。

复杂度O(nlog(n)),海星。但是筛素数往往只是解题的一小步,这个复杂度有点高了。

2.Eratosthenes筛法:

第一种方法由于要挨个判断,复杂度高了些。为了避免不必要的判断,我们可以在找出一个素数时,筛掉它所有的倍数。这样复杂度降到了O(nloglog(n)),非常接近线性。

(lyd:卡这种常数的简直是丧病)

代码实现也很简单:

#include<iostream>
#include<cstdio>
#define maxn 1000005
using namespace std;
int v[maxn],pri[maxn];
int m=0,n;
void primes(int n)
{
    for(int i=2;i<=n;i++){
        if(v[i])continue;
        printf("%d ",i);
        for(int j=1;j<=n/i;j++){
            v[i*j]=1;
        }
    }
}
int main()
{
    scanf("%d",&n);
    primes(n);
}

3.线性筛素数

虽然上一种筛法已经比较优秀了,但还不是线性。

现在如果要重新开辟一条思路,比较难。所以我们考虑,在筛法2中做了哪些无用功。

合数会被它的每一个质因子筛。

如果线性筛法,应该找一个办法,使得每一个合数只被筛一次,这样就保证了线性的复杂度。

最经典的做法就是让每一个合数只被它最小的质因子筛掉。

一个例子:n=10;

代码(一会会跑一遍):

#include<iostream>
#include<cstdio>
#define maxn 1000005
using namespace std;
int v[maxn],pri[maxn];
int m=0,n;
void primes(int n)
{
    for(int i=2;i<=n;i++){
        if(!v[i]){
            v[i]=i;
            pri[++m]=i;
        }
        for(int j=1;j<=m;j++){
            if(pri[j]>v[i]||pri[j]>n/i)break;
            v[i*pri[j]]=pri[j];
        }
    }
} 
int main()
{
    scanf("%d",&n);
    primes(n);
    for(int i=1;i<=m;i++)printf("%d ",pri[i]);
}

模拟过程(水印遮掉的部分都是2,3,5,7):

好的,线性筛就完了。

欧拉函数

问题:给定n,问1~n中与n互质的数的个数。

欧拉函数常用 \varphi (n) 表示。

                                    \varphi (n)=n* \frac{​{p}_1-1}{​{p}_1}*\frac{​{p}_2-1}{​{p}_2}*...*\frac{​{p}_m-1}{​{p}_m}=n*{\prod }_{p|n}(1-\frac{1}{p})

 

按照上方筛法2,p会筛掉 n/p 个数,q会筛掉 n/q 个数,

根据容斥原理,最后剩下的个数应该是       n-\frac{n}{p}-\frac{n}{q}+\frac{n}{pq}=n(1-\frac{1}{p})(1-\frac{1}{q})​​​​​​​

依次类推,证毕。

根据以上,可以通过分解质因数来求欧拉函数。

但很多时候都要求1~n的欧拉函数,这就需要线性推出欧拉函数。

首先要明确一些东西:

欧拉函数是积性函数,即:                          \varphi (ab)=\varphi(a)*\varphi(b) (gcd(a,b)==1)

于是就有了一些引理:

1.若 p|n 且 p^2|n\varphi (n)=\varphi(n/p)*p

2.若 p|n 且 p^2 不整除 n , \varphi (n)=\varphi(n/p)*(p-1)

证明:

1.把 n 和 ( n / p ) 分解质因数,公式连乘部分相同,只是'n'的部分多了p,所以成立。

2.此时 n 和 ( n / p ) 互质,根据积性函数可得引理2。

有了这两个引理,就可以在线性筛的时候顺便筛欧拉函数了。

#include<iostream>
#include<cstdio>
#define maxn 1000005
using namespace std;
int v[maxn],pri[maxn],phi[maxn];
int m=0,n;
void primes(int n)
{
    for(int i=2;i<=n;i++){
        if(!v[i]){
            v[i]=i;
            pri[++m]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=m;j++){
            if(pri[j]>v[i]||pri[j]>n/i)break;
            v[i*pri[j]]=pri[j];
            phi[i*pri[j]]=phi[i]*(i%pri[j]?pri[j]-1:pri[j]);
        }
    }
} 
int main()
{
    scanf("%d",&n);
    primes(n);
    for(int i=1;i<=m;i++)printf("%d ",pri[i]);
    cout<<endl;
    for(int j=1;j<=n;j++)printf("%d ",phi[j]);
}

我数学怎么这么菜啊

​​​​​​​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值