质数的定义
质数又称作素数.
质数是除了1和本身没有其他因数的数.
与之相反的是合数.
1不属于质数或合数.
质数的判断
假设我们要判断(n)是不是质数.
从2枚举(n-1),若能整除(n),(n)就不是质数.
时间复杂度(O(n)).
但因数是成对的(除了(sqrt n))
令(n=c_1 c_2),假设(c_1 le c_2)
则(c_1 le sqrt n , c_2 ge sqrt n)
所以只需枚举到(sqrt n).
bool isprime(long long x) {
for(int i=2; i<=sqrt(x); i++) {
if(x%i==0) return false;
}
return true;
}
时间复杂度(O(sqrt n)).
质数筛法
现在我们要求(1)到(n)中全部质数.
暴力
最简单的办法就是判断每个数是否为质数.
时间复杂度(O(n sqrt n)).
埃氏筛法
从(1)到(sqrt n),若这个数没有被标记,即为质数.
只要发现了一个质数(t),就把从(1)到(n)中全部(t)的倍数标记.
时间复杂度(O(n log n))
欧拉线性筛法
对于每个(i(1<i<n)),筛去所有小于(i)的质数与(i)的乘积.
不难发现,有些合数被重复筛去.
设所有小于(i)的质数分别为(p_1,p_2,...,p_j).
当(i\%p_k=0)时,停止筛(i)的倍数,即可避免重复.
因为当(l>k)时,所有(i*p_l)都会被比(i)大的数与(p_k)的乘积筛去.
for(int i=2;i<=n;i++){
if(!vis[i]) {
cnt++;
pri[cnt]=i;
}
for(int j=1;i*pri[j]<=n;j++){
vis[i*pri[j]]=true;
if(i%pri[j]==0) break;
}
}
时间复杂度(O(n))
质因数分解
一个数的质因数必定小于等于(sqrt n).
只需把(i)从(2)枚举到(sqrt n),从(n)中去掉所有(i)并做记录,就能求出(n)的质因数.
for(int i=2;i<=sqrt(n);i++) {
while(n%i==0) {
n/=i;
cnt[i]++;
}
}
时间复杂度(O(sqrt n))
欧拉函数
欧拉函数是从(1)到(n-1)的数中与(n)互质的数的个数.
(φ(1)=φ(2)=1).
性质
- 对于质数(n),(φ(n)=n-1).
- 对于质数(p),(φ(p^k)=(p-1)*p^{k-1}).
- 对于互质的(n,m),(φ(n)*φ(m)=φ(n*m)).
- 对于奇数(n),(φ(2*n)=φ(n)).
- 对于(n(n>1)),(sum_{k=1}^n{k(gcd(n,k)=1)}=φ(n)*n/2).
- 若(k\%p=0),(φ(k*p)=φ(k)*p).
- 若(k\%p!=0),(φ(k*p)=φ(k)*(p-1)).
- 对于互质的(a,m),(a^{φ(p)} equiv 1 pmod {p}).
- (sum_{kmid n}^n{φ(k)}=n)
- (φ(n)=sum_{kmid n}^n{φ(k)*frac{n}{k}}).
计算公式及证明
设(n)的质因数分解为(p_1^{c_1}*p_2^{c_2}*)...(*p_i^{c_i}).
则(φ(n)=n*frac{p_1-1}{p_1}*frac{p_2-1}{p_2}*)...(*frac{p_i-1}{p_i})
证明:设(p,q)是(n)的质因子.
则(1)-(n)中,(p)的倍数有(frac{n}{p})个,(q)的倍数有(frac{n}{q})个,(p*q)的倍数有(frac{n}{p+q})个.
所以(1)到(n-1)中与(p,q)互质的数的个数为
(egin{align*} n-frac{n}{p}-frac{n}{q}+frac{n}{p*q}&=n*(1-frac{1}{p}-frac{1}{q}+frac{1}{p*q})\ &=n*(1-frac{1}{p})*(1-frac{1}{q}) end{align*})
其他质因子同理.
算法实现
对于求单个欧拉函数,分解质因数即可.
for(long long i=2;i<=sqrt(num);i++) {
bool div=false;
while(num%i==0) {
div=true;
num/=i;
}
if(div) ans=ans*(i-1)/i;
}
if(num>1) ans=ans*(num-1)/num;
时间复杂度与质因数分解一样.
但如果要求从(1)到(n)的数的欧拉函数,就可以通过打表法来求解.
for(int i=2;i<=MAXN;i++) {
if(!phi[i]) {
for(int j=i;j<=MAXN;j+=i) {
if(!phi[j]) phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
或是在欧拉筛时同时求出欧拉函数.
for(int i=2;i<=n;i++){
if(!vis[i]) {
cnt++;
pri[cnt]=i;
phi[cnt]=i-1;
}
for(int j=1;i*pri[j]<=n;j++){
vis[i*pri[j]]=true;
if(i%pri[j]==0) {
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*(pri[j]-1);
}
}