[数论学习笔记]费马小定理、欧拉函数、欧拉定理、欧拉降幂公式
前置知识
完全剩余系
百度百科:
从模n的每个剩余类中各取一个数,得到一个由n个数组成的集合,叫做模n的一个完全剩余系。
简单点说,n的完全剩余系就是0到n-1的集合。
缩剩余系
又叫简化剩余系。
简单点说,n的缩剩余系就是其完全剩余系中与n互质的数组成的一个集合。
费马小定理
内容:
证明:
考虑p的缩剩余系,因为p是质数,所以p的缩剩余系为 \(\{1,2,3,\cdots,p-1\}\)
把缩剩余系中的每个数乘上一个数k(要求:\(\gcd(k,p)=1\) ),所得到的集合在模p意义下仍是p的缩剩余系。
上一句如何证明?
只需证明在新集合中任意两个数都不相等即可。
反证:取出两个数 \(k\times a_1,k\times a_2\),若这两个数在模p移一下相等,则
移项得:
即:
又因为
所以得出
而
所以
不成立。
所以所得到的集合在模p意义下仍是p的缩剩余系。
这时候就有下面这个式子:
化简一下可得:
因为
所以两边约去后得
证毕。
应用
求逆元
而逆元应用非常广泛。
欧拉函数
定义
欧拉函数\(\psi(x)\)表示小于x的数字中与x互质的数的个数。
公式
其中n表示x的质因数个数。
证明
考虑容斥原理。
设 \(p_1,p_2\) 为 \(x\) 的两个质因数,则 \(p_1\)的倍数有 \(\frac{x}{p_1}\) 个,\(p_2\) 的倍数有 \(\frac{x}{p_2}\) 个。
把这些数删去,还剩下 \(x-\frac{x}{p_1}-\frac{x}{p_2}+\frac{x}{p_1\times p_2}\) 个。(多删了 \(\frac{x}{p_1\times p_2}\) 个)
然后就是化简变形:
推广到n个质因数,得到公式:
性质
积性函数。
满足:
若p是质数,则:
线性求代码实现
线性筛可是很NB的,以至于所有积性函数都怕他(掩盖不了看似大的惊人的复杂度了)。
————某谷题解
也就是说,所有线性求积性函数的关键就是用最小的质因子求。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100005;
int phi[maxn],cnt,prime[maxn],vis[maxn],n;
int main(){
cin>>n;
phi[1]=1;//初始化
for(int i=2;i<=n;i++){
if(!vis[i]) prime[++cnt]=i,phi[i]=i-1;//筛质数、质数i的欧拉函数值为 i-1
for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
vis[i*prime[j]]=1;//合数
if(i%prime[j]==0){//效率关键
phi[i*prime[j]]=phi[i]*prime[j];
break;
}else{
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
for(int i=1;i<=n;i++) cout<<phi[i]<<" ";
return 0;
}
欧拉定理
内容:
证明:
欧拉定理算是费马小定理的拓展,所以证明很像。
考虑 \(m\) 的缩剩余系,很显然缩剩余系中元素个数为 \(\psi(m)\)。
然后把缩剩余系中的每个数乘上一个数 \(a\),要求 \(\gcd(a,m)=1\)。
根据费马小定理那里证明的命题,新的集合仍是 \(m\) 的缩剩余系。
假设原来的缩剩余系为 \(p\),乘上 \(a\) 以后的缩剩余系为 \(q\),则有:
即:
因为 \(\gcd(\prod_{i=1}^{\psi(m)}p_i,m)=1\),所以两边可以同时约掉,得:
证毕。
欧拉降幂公式
又叫做拓展欧拉定理。
有一道板子题:
传送门
内容:
证明:
略。(挺实用的,一定要背过)
注意当 \(b\ge \psi(m)\) 时才能用公式,否则直接快速幂。
AC代码
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
long long a,m,mm;
long long phi,res;
int len,yyy;
string s;
long long fp(long long a,long long b){
if(b==1) return a;
long long now=fp(a,b/2);
if(b&1) return now*now%m*a%m;
return now*now%m;
}
int main()
{
cin>>a>>m;
phi=mm=m;
for(int i=2;mm>1&&i<=sqrt(m);i++){
if(!(mm%i)){
phi/=i;
phi*=(i-1);
while(!(mm%i)) mm/=i;
}
}
if(mm>1) phi=phi/mm*(mm-1);
cin>>s;
len=s.length();
for(int i=0;i<len;i++){
res=res*10+s[i]-'0';
if(res>=phi) res%=phi,yyy=1;
}
if(yyy) res=res+phi;
cout<<fp(a%m,res);
return 0;
}