逆元:
定义:对于正整数a,如果有 ax≡1(mod m),注意这里的等号代表恒等,那么把这个同余方程中的最小正整数解x叫作a模m的逆元(其实相当于 x*x^-1=1的变式)。
求逆元常用的有三种方法:扩展欧几里德算法、费马小定理、欧拉定理求逆元。当然还有其他的办法,但是以下只对以上三个进行介绍。
扩展欧几里德算法:
欧几里得算法返回的是两个数的最大公约数,而扩展欧几里德算法返回的也是最大公约数 不过所传的参数多了而已。
定义:用来在已知a,b求解一组x、y使得ax+by=gcd(a,b)(此式子的解一定存在,依据数论中的知识)
常用在求解模线性方程及方程组中。
关于解x、y的方法的理解: 已知方程ax+by=gcd(a,b)
(1)当b=0,gcd(a,b)=a,此时x=1,y=0; 1a+0b=a
(2)设ax1+by1=gcd(a,b)
在下一个exgcd中 bx2+(a mod b)y2=gcd(b,a mod b)
根据欧几里得原理可知 gcd(a,b)=gcd(b,a mod b)
则 ax1+by1=bx2+(a mod b)y2
即ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;
则x1=y2,y1=x2-(a/b)y2;
所以可以得出x1,y1的值基于x2,y2。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=200;
char s[N];
char b[N];
int exgcd(int a,int b,int &x,int &y)//扩展gcd
{
if(!b)//b为0的情况
{
x=1;
y=0;
return a;
}
int r=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return r;
}
int main()
{
ios::sync_with_stdio(false);
int a,b;
cin>>a>>b;
int x=0,y=0;
cout<<exgcd(a,b,x,y)<<endl;
return 0;
}
1.扩展gcd求逆元要求 a,m互质
求出的逆元是最小逆元
先给出互质的概念,n个数(n>=2)的公因数只有1.
质数又称之为素数。
先给出数学过程:
ax+by=gcd(a,b);
则ax+my=gcd(a,m);
因为a,m互质 所以 gcd(a,m)=1
则 ax+my=1
即 ax≡1(mod m)
此时x为a的逆元。
代码:用扩展gcd求出一组 x,y,gcd;若gcd为1 则逆元存在,将x调整到0~m-1的范围内;否则逆元不存在
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=200;
char s[N];
char b[N];
int exgcd(int a,int b,int &x,int &y)
{
if(a==0&&b==0)
return -1;
if(!b)
{
x=1;
y=0;
return a;
}
int d=exgcd(b,a%b,y,x); //回代 注意这里的与扩展gcd的不同
y-=a/b*x;
return d;
}
int main()
{
ios::sync_with_stdio(false);
int m,n;
cin>>m>>n;
int x,y;
int d=exgcd(m,n,x,y);
cout<<(x%n+n)%n<<endl;
return 0;
}
2.费马小定理求逆元要求m为素数,且a,m互质
费马小定理: a^(m-1)≡1 (mod m) //m为素数
a^(m-1)≡ 1 (mod m) 则a*a^(m-2)≡1 (mod m)
故 a^(m-2)为a在模m下的逆元(用快速幂求解即可)
时间复杂度 logN。
代码:
int mod;
int quick_mod(int n,int p)
{
int res=1,k=p;
while(k)
{
if(k%2)
res=res*n%mod;
n=n*n%mod;
k/=2;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
int a;
cin>>a>>mod;
cout<<quick_mod(a,mod-2)<<endl;
return 0;
}
3.欧拉定理求逆元:基本通用
时间复杂度 lg(√n)。
欧拉定理: a^(∅ (m))≡ 1 (mod m)
则 a*a^(∅ (m)-1)≡ 1 (mod m)
即 ax≡ 1 (mod m)
故 a^(∅ (m)-1)是a的逆元 ∅ (m)是小于m且与m互质的数的个数
1.如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系
2.对于素数p Φ (p)=p-1,对于两个素数pq ,Φ (pq)=pq-1
欧拉函数是积性函数,但不是完全积性函数
3.如果n是质数的某一个次方,即 n = 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互质的数。
上面的式子还可以写成下面的形式:
4.如果n可以分为两个互质的整数的乘积,n=p1*p2
则Φ (n)=Φ (p1)*Φ (p2)
5.任意一个数n(n>1)都可以写成一系列质数的积
根据4的结论,得到
再根据3的结论,得到
故
下面给出两种求欧拉数的代码:
直接求欧拉:
int euler(int x)
{
int res=x;
for(int i=2;i*i<=x;i++)
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0)
x/=i;
}
if(x>1)
res=res/x*(x-1);
return res;
}
也可以先打表直接使用,过程是想让每个phi【i】=i,再对每个质数p,j为它的倍数,phi【j】=phi【j】/p*(p-1);
void eulerplus()
{
for(int i=1;i<=N;i++)
phi[i]=i;
for(int i=2;i<=N;i+=2)
phi[i]/=2;
for(int i=3;i<=N;i+=2)
if(phi[i]==i)
{
for(int j=i;j<=N;j+=i)
phi[j]=phi[j]/i*(i-1);
}
}
4.逆元的应用 求模逆元
ax≡ 1 (mod m)
故 x=1/a(mod m)
故 (b/a)mod m
则(bx)mod m
则(b%mx%m)%m
striling公式
常用于求n!近似值
n!≈ √2πn(n/e)^n
emmm 还是上图片吧
该知识点可以与lg连用估算某数的位数
n进制数x的位数=logn(x)+1
例如十进制数x的位数=lg(x)+1
还可以估算某数的阶乘是另一个数的倍数。