同余
同余
给定一个正整数m,如果两个整数a和b满足a-b能够被m整除m|(a-b),那么整数a与b对模m同余,记为a≡b (mod m)
举例a=100,b=60,a-b=40,m=8,8|40,故100≡60 (mod 40)。
同余另一种说法是a%m==b%m。
主要性质
如果a≡b (mod m),c为整数则
a+c≡b+c (mod m)
a-c≡b-c (mod m)
a*c≡b*c(mod m)
(a+b) mod m=((a mod m)+(b mod m)) mod m
(a-b) mod m=((a mod m)-(b mod m)) mod m
(a*b) mod m=((a mod m)*(b mod m)) mod m
对于减法(a-b)%m=(a-b+m)%m
逆元
(1)定义:
就是一个数的倒数,那为什么要求一个数的倒数:比如a/b这个时候b的值特别大,就是导致double精度不够所以我们要将a/b换成a*c,其中 。
设c是b关于模m的逆元,则有b*c ≡1(mod m);
推论:(a/b)mod m=(a/b)*1 mod m=(a/b)*b*c mod m=a*c mod m;即a/b的模等于a*(b的逆元)。
逆元的求法
费马小引理求解逆元
费马小引理:如果a是不能被质数p整除的正整数则有ap-1 ≡1(mod m)。
推导:a与ap-2互为逆元。
#include<iostream> using namespace std; long long quickpow(long long a,long long b,long long mod) { long long ret=1; a%=mod; while(b) { if(b & 1 ) ret = ( ret *a ) % mod; a = (a * a)% mod; b>>=1; } return ret; } //mod必须为素数,且(a,mod)=1 long long inv(long long a,long long mod) { return quickpow(a,mod-2,mod); } int main() { int n,m; cin>>n>>m; cout<<inv(n,m)<<endl; return 0; }
扩展欧几里得求逆元
算法的作用:求解a,b的最大公约数m和a*x+b*y=m的一个解。
求a*x ≡1 (mod m) 等价求a*x+m*y=1,可以用扩展殴几里得求得一组解,(x+m) %m 就是a的逆元。条件是(a,m)=1。
#include<iostream> using namespace std; typedef long long ll; void extgcd(ll a,ll b,ll& d,ll& x,ll& y) { if(!b) { d=a;x=1;y=0; } else { extgcd(b,a%b,d,y,x); y-=x*(a/b); } } int ModularInverse(int a,int b) { ll d,x,y; extgcd(a,b,d,x,y); return d==1?(x+b)%b:-1; //返回的结果就是(1/a)mod(b)的结果 // complete this part } int main() { int x,y; cin>>x>>y; cout<<x<<"的模"<<y<<"的逆元:"<<ModularInverse(x,y)<<endl; cout<<y<<"的模"<<x<<"的逆元:"<<ModularInverse(y,x)<<endl; return 0; }
线性求逆元
来看带余除法 式子 p=k*i+r
我们可以写成 k*i+r≡0(mod p)
式子两边同乘 i-1*r-1 (i-1,r-1皆为模p意义下的逆元)
所以我们有 k*r-1+i-1≡0(mod p)
i-1≡-k*r-1(mod p)
i-1≡-(p/i)*(p%i)-1(mod p)
所以i-1可以用(p%i)-1推出,所以就可以用递推式求出来1到i之间所有数的逆元。
#include<iostream> using namespace std; const int MAXN=100010; //1、线性求逆元 int inv[MAXN]; void INV(int a,int p)//线性求到a的逆元 { inv[1] = 1; for (int i=2; i<=a; ++i) inv[i] = (-(p/i))*inv[p%i]%p; } //2、单独求某个值的逆元 int INVd(int a,int p)//线性求a的逆元 { if (a==1) return 1; return ((-(p/a)*INVd(p%a,p))%p); } int main() { int a,p; cin>>a>>p; INV(a,p); for (int i=1;i<=a;i++) { cout<<(inv[i]+p)%p<<" "; } cout<<endl; for (int i=1;i<=a;i++) { cout<<(INVd(i,p)+p)%p<<" "; } cout<<endl; return 0; }