( 应acm协会的以往的习惯,每届学长学姐都要给下一届的学弟们(对,我们的下一届没有学妹)讲一个知识点,菜菜的我没什么别的选择,只能硬着头皮给他们讲数论基础,历时整整一天半,终于把课件以及要讲的东西捋清楚了TVT。虽然真正讲课的时候 垮了,但是自己也真的把这些平常看着头大的定理理清楚了!!!)
好了进入正题:
一、欧几里德算法(这里直接供上ppt内容)
二、扩展欧几里德算法
这个算法其实就是用来解二元一次方程的一组满足方程的(x,y)。主要的思想就是通过迭代来找解,每次的结果都作为下一次的初始值,直到达到满足条件为止。至于那个二元一次方程则为:ax+by=gcd(a,b) (ax+by=z)
此处引入一个引理:裴蜀定理:若ax+by = z,则 gcd(a,b)| z 。所以求出x,y以后再乘上 就是括号里方程的解了
(式子看起来比较高级,其实也很简单,把gcd(a,b)|z 换成另一种形式 应该就好理解了)
接下来就是扩展欧几里德算法的具体实现:
(这个图片上的内容其实就是从百度百科上复制下来的,因为挺好懂的也就没有再加什么注释了)
上代码:
//扩展欧几里得算法
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
long long ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
三、费马小定理(欧拉儿子定理)
费马小定理:如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p )。
(欧拉定理:对于任意互素的a和n,有a^φ(n) ≡1(mod n ) 其中φ(n)指的是1~n中与n互质的数的个数,以下是对欧拉定理的理解)
几个与逆元相关的定理到这就讲完了,接下来就进入正题!!!
逆元:(单纯理解就是一个数的倒数 a*b=1 b就是a的倒数 不过不能这么去记,貌似这样是不对的,想要科学官方的解释可以去百度百科搜)它是用来解决(a/b)%m (a很大,b也很大)这样的问题。由于带了除的模运算并没有对应的运算规则,所以就将其转换成 (a*b')%m=((a%m)*(b'%m))%m 这里b'是b在mod m意义下的逆元。不过要注意:只有当b和m互质的时候才存在逆元。
那么逆元又应该怎么求呢???
方法一:扩展欧几里德算法
原理:a∗b≡1(mod p) => a∗b+k∗p=1 (把b看做x,k看做y,将此二元一次方程解出来一个解就好)
//求a在mod下的逆元,不存在逆元返回-1
long long getInv1(int a,int mod){
long long x,y;
long long d=exgcd(a,mod,x,y);//上面提到的扩展欧几里德算法
return d==1?(x%mod+mod)%mod:-1;//防止x小于0
}
方法二:费马小定理
由费马小定理有 a^(p−1)≡1(mod p) => a^(p−2)∗a≡1(mod p) => a^(p−2)就是a在mod p意义下的逆元
//快速幂求a的p次方 (mod m)
long long qkpow(long long a,long long p,long long m)
{
long long t=1,tt=a%m;
while(p)
{
if(p&1)t=t*tt%m;
tt=tt*tt%m;
p>>=1;
}
return t;
}
long long getInv2(long long a,long long mod)
{
return qkpow(a,mod-2,mod);
}
方法三:递归或 递推 (其中递推又称逆元的线性解法)
//求a关于p的逆元,注意:a要小于p,最好传参前先把a%p一下
long long getInv3(long long a, long long p) {
return a == 1 ? 1 : (p - p / a) * getInv3(p % a, p) % p;
}
long long inv[9978];
void getInv4(long long mod)
{
inv[1]=1;
for(int i=2;i<mod;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
//这里用的公式我并不是很懂,如果哪位大佬知道的话
//可以指教一下 OVO
好了到这就结束了,希望能给大家带来帮助