欧拉函数学习笔记
原理
请思考以下问题:
任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?(比如,在1到8之中,有多少个数与8构成互质关系?)
计算这个值的方法就叫做欧拉函数,以φ(n)表示。在1到8之中,与8形成互质关系的是1、3、5、7,所以 φ(n) = 4。
φ(n) 的计算方法并不复杂,但是为了得到最后那个公式,需要一步步讨论。
第一种情况
如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系。
第二种情况
如果n是质数,则 φ(n)=n-1 。因为质数与小于它的每一个数,都构成互质关系。比如5与1、2、3、4都构成互质关系。
第三种情况
如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数),则
\(\phi\left(p^{k}\right)=p^{k-p^{k-1}}\)
比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。
这是因为只有当一个数不包含质数p,才可能与n互质。而包含质数p的数一共有p(k-1)个,即1×p、2×p、3×p、...、p(k-1)×p,把它们去除,剩下的就是与n互质的数。
上面的式子还可以写成下面的形式:
\(\phi\left(p^{k}\right)=p^{k-p k-1}=p^{k}\left(1-\frac{1}{p}\right)\)
可以看出,上面的第二种情况是 k=1 时的特例。
第四种情况
如果n可以分解成两个互质的整数之积,
n = p1 × p2
则
φ(n) = φ(p1p2) = φ(p1)φ(p2)
即积的欧拉函数等于各个因子的欧拉函数之积。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24。
这一条的证明要用到"中国剩余定理",这里就不展开了,只简单说一下思路:如果a与p1互质(a<p1),b与p2互质(b<p2),c与p1p2互质(c<p1p2),则c与数对 (a,b) 是一一对应关系。由于a的值有φ(p1)种可能,b的值有φ(p2)种可能,则数对 (a,b) 有φ(p1)φ(p2)种可能,而c的值有φ(p1p2)种可能,所以φ(p1p2)就等于φ(p1)φ(p2)。
第五种情况
因为任意一个大于1的正整数,都可以写成一系列质数的积。
\(n=p_{1}^{k_{1}} p_{2}^{k_{2} \ldots p_{r} r}\)
根据第4条的结论,得到
\(\phi(n)=\phi\left(p_{1}^{\left.k_{1}\right)} \phi\left(p_{2}^{k_{2}}\right) \ldots \phi\left(p_{r r}^{k_{r}}\right)\right.\)
再根据第3条的结论,得到
\(\phi(n)=p_{1}^{k_{1}} p_{2}^{k_{2}} \ldots p_{r}^{k_{r}} \left(1-\frac{1}{p_{1}}\right)\left(1-\frac{1}{p_{2}}\right) \ldots\left(1-\frac{1}{p_{r}}\right)\)
也就等于
\(\phi(n)=n\left(1-\frac{1}{p_{1}}\right)\left(1-\frac{1}{p_{2}}\right) \ldots\left(1-\frac{1}{p_{r}}\right)\)
这就是欧拉函数的通用计算公式。比如,1323的欧拉函数,计算过程如下:
\(\phi(1323)=\phi\left(3^{3} \times 7^{2}\right)=1323\left(1-\frac{1}{3}\right)\left(1-\frac{1}{7}\right)=756\)
性质
(1)对于质数\(n,φ(n)=n−1\)
(2)对于\(n=p^k,φ(n)=(p−1)∗p^{k−1}\)
(3)对于 \(n=\prod p_{i}^{k_{i}}\),\(\varphi(n)=n * \prod\left(1-\frac{1}{p_{i}}\right)\)
(4) 对于\(gcd(n,m)=1\),\(\varphi(n * m)=\varphi(n) * \varphi(m)\)
(5) 【欧拉定理】 对于互质的\(a,m\), \(a^{\varphi(m)} \equiv 1 \quad(\bmod m)\)
(6) 小于n且与n互质的数的和:\(S=n * \frac{\varphi(n)}{2}\)
(7) 对于质数p
若\(n \quad \bmod p=0\), $ φ(n∗p)=φ(n)∗p$,
若\(n \quad \bmod p \neq 0\),\(\varphi(n * p)=\varphi(n) *(p-1)\)
(8)\(\sum_{d \mid n} \varphi(d)=n\)
\(\varphi(n)=\sum_{d \mid n} \mu(d) * \frac{n}{d}\)
(9)【欧拉降幂公式】
\(a^{b} \equiv\left\{\begin{array}{ll}a^{b \% \phi(p)} & g c d(a, p)=1 \\ a^{b} & g c d(a, p) \neq 1, b<\phi(p) \\ a^{b \% \phi(p)+\phi(p)} & g c d(a, p) \neq 1, b \geq \phi(p)\end{array} \quad(\bmod p)\right.\)
模板
直接质因数分解法
//POJ 2407,给定n,求小于n且和n互质的数个数
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
const ll maxp=1e5+5;
bool isPrime[maxn];
ll Prime[maxp], primecnt = 0;
void GetPrime(ll n){//筛到n
memset(isPrime, 1, sizeof(isPrime));
isPrime[1] = 0;//1不是素数
for(ll i = 2; i <= n; i++){
if(isPrime[i])//没筛掉
Prime[++primecnt] = i; //i成为下一个素数
for(ll j = 1; j <= primecnt && i*Prime[j] <= n; j++) {
isPrime[i*Prime[j]] = 0;
if(i % Prime[j] == 0)//i中也含有Prime[j]这个因子
break;
}
}
}
ll getol(ll n){
if(n==0){
return 0;
}
ll ans=n;
ll temp=n;
for(ll i=1;i<=primecnt;i++){
if(temp%Prime[i]==0){
ans=ans*(Prime[i]-1)/Prime[i];
while(temp%Prime[i]==0)
temp/=Prime[i];
}
if(temp<Prime[i]*Prime[i]){
break;
}
}
if(temp>1){
ans=ans*(temp-1)/temp;
}
return ans;
}
int main () {
GetPrime(maxn-5);
ll n;
while(~scanf("%lld",&n)){
if(n==0){
break;
}
ll ans=getol(n);
printf("%lld\n",ans);
}
}
线性素数筛法
原理:
- 若i%p=0,p是素数,那么\(φ(i*p)=φ(i)*p\)
- 若i%p!=0,p是素数,那么\(φ(i*p)=φ(i)*(p-1)\)
证明:
2式可以直接由积性函数的性质(性质4)推出,1式证明如下:
将\(i\)转换为\(p^n*k\)的形式,则\(φ(i)=p^{n}*\frac{p-1}{p}*φ(k)\),\(φ(i*p)=φ(p^{n+1}*k)\),由于\(p^{n+1}\)和\(k\)互质,因此\(φ(i*p)=φ(p^{n+1}*k)=φ(p^{n+1})*φ(k)=p^{n+1}*\frac{p-1}{p}*φ(k)=p*φ(i)\)
代码:
//HDU1286,T组数据,询问n的欧拉函数,n<32768
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int maxp=1e6+5;
bool isprime[maxn];
int prime[maxp], primecnt = 0;
int ol[maxn];
void getOL(int n){//筛到n
memset(isprime, 1, sizeof(isprime));
isprime[1] = 0;//1不是素数
ol[1]=1;
for(int i = 2; i <= n; i++){
if(isprime[i]){//没筛掉
prime[++primecnt] = i; //i成为下一个素数
ol[i]=i-1;
}
for(int j = 1; j <= primecnt && i*prime[j] <= n; j++) {
isprime[i*prime[j]] = 0;
if(i % prime[j] == 0){//i中也含有prime[j]这个因子
ol[i*prime[j]]=ol[i]*prime[j];
break;
}
else{
ol[i*prime[j]]=ol[i]*(prime[j]-1);
}
}
}
}
int main(){
getOL(32768);
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
printf("%d\n",ol[n]);
}
}
引用与参考: