数论之逆元(单位元,逆元,扩展欧几里得定理,费马小定理,快速幂,高精度阶乘的小技巧,以及逆元的一些应用)

#本篇文章将比较长,请耐心观看#

单位元

要说明逆元,就要先说明单位元。单位元是集合中的一种特别的元,当它与其他元素结合时不会改变其他元素。a*e = a(其中的“*”是通配符,不单指乘法,可以代表+ - * /等其他运算符号.),那么我们说e就是单位元。
借助定义,我们就可以算出每个运算的单位元是什么。

加法的单位元

a + e = a ⇒ e = 0 a+e = a \Rightarrow e = 0 a+e=ae=0,所以加法的单位元为0;

减法的单位元

a − e = a ⇒ e = 0 a-e=a\Rightarrow e=0 ae=ae=0,所以减法的单位元也为0;

乘法的单位元

a ∗ e = a ⇒ e = 1 a*e=a\Rightarrow e = 1 ae=ae=1,所以乘法的单位元为1;

除法的单位元

a / e = a ⇒ e = 1 a/e=a\Rightarrow e=1 a/e=ae=1,所以除法的单位元为1;

逆元

定义:在一个集合中,对于某种运算*;如果任意两个元素的运算结果等于单位元,则称这两个元素互为逆元。

加法的逆元

a + e = 0 ⇒ e = 0 a+e = 0 \Rightarrow e = 0 a+e=0e=0,所以加法的逆元为-a;

减法的逆元

a − e = 0 ⇒ e = 0 a-e=0\Rightarrow e=0 ae=0e=0,所以减法的逆元为a;

乘法的逆元

a ∗ e = 1 ⇒ e = 1 a*e=1\Rightarrow e = 1 ae=1e=1,所以乘法的逆元为 1 a \frac{1}{a} a1

除法的逆元

a / e = 1 ⇒ e = 1 a/e=1\Rightarrow e=1 a/e=1e=1,所以除法的逆元为a;

其实加法逆元就是相反数,乘法逆元就是倒数。


以上的四种运算法则很好理解,但是实际题目中并不多见,而最多见的当是模乘了。

(这里先说明取模和取余的区别)

假设a与b互质且a>b.
则有 a = k b + r a = kb + r a=kb+r; 这里r为余数。
所以可以知道 r = a − a b b r = a - \frac{a}{b}b r=abab
当我们要取模时, a b \frac{a}{b} ba向负无穷大取整。
当我们要取余时, a b \frac{a}{b} ba向0取整。
所以取模的(正负)符号和a(被除数)有关,而余数恒大于0;

模乘的单位元与逆元

我们先来证明ab(mod n) = a(mod n) * b(mod n);
a = k 1 n + r a , b = k 2 n + r b a = k_1n+r_a,b = k_2n+r_b a=k1n+ra,b=k2n+rb
⇒ a b = k 1 k 2 n 2 + k 1 r b n + k 2 r a n + r a r b \Rightarrow ab=k_1k_2n^2+k_1r_bn+k_2r_an+r_ar_b ab=k1k2n2+k1rbn+k2ran+rarb
⇒ a b ≡ r a r b ( m o d   n ) \Rightarrow ab\equiv r_ar_b(mod\ n) abrarb(mod n)
⇒ a ≡ r a ( m o d   n ) , b ≡ r b ( m o d   n ) \Rightarrow a \equiv r_a(mod\ n),b\equiv r_b(mod\ n) ara(mod n),brb(mod n)
⇒ a b ( m o d   n ) = a ( m o d   n ) ∗ b ( m o d   n ) \Rightarrow ab(mod\ n) = a(mod\ n)*b(mod\ n) ab(mod n)=a(mod n)b(mod n)

模乘的单位元

a e = a ( m o d   n ) ⇒ e = 1 ae = a(mod\ n) \Rightarrow e = 1 ae=a(mod n)e=1

模乘的逆元

a e = 1 ( m o d   n ) ⇒ e = a − 1 ae = 1(mod\ n) \Rightarrow e=a^{-1} ae=1(mod n)e=a1
那么问题来了,模乘的逆元怎么计算?

计算模乘逆元的方法

扩展欧几里得定理

也许你不知道欧几里得定理是什么,但我相信你肯定接触过,欧几里得定理就是我们求两数最大公约数的辗转相除法。
为了让扩展欧几里得定理更加形象,我们先看一个例题

给定正整数a和b,求满足等式ax+by=1的x的最小正整数解。如果不存在返回-1

我们先考虑x不存在的情况,我们通过欧几里得定理可以求出a和b的最大公约数为k
所以ax+by=1可以写成 k ( a k x + b k y ) = 1 k(\frac{a}{k}x+\frac{b}{k}y) = 1 k(kax+kby)=1两个正整数相乘结果为1只能是1*1,所以k = 1,而k为a和b的最大公约数,所以a和b互质。
(ps:可能有人不知道为什么 ( a k x + b k y ) (\frac{a}{k}x+\frac{b}{k}y) (kax+kby)和k是两个正整数啊,由于k为a和b的最大公约数,所以k一定是正整数,并且k能被a和b整除,而x,y也为整数,
所以 ( a k x + b k y ) (\frac{a}{k}x+\frac{b}{k}y) (kax+kby)为整数(当然不一定为正,但k一定是正数,想要结果为1, ( a k x + b k y ) (\frac{a}{k}x+\frac{b}{k}y) (kax+kby)也必须要为正数))
那么当a和b不互质时,x不存在,直接返回-1;


现在考虑这个题目和上面介绍的逆元有什么关系?
上文说道模乘的逆元即: a e ≡ 1 ( m o d   n ) ae\equiv1(mod\ n) ae1(mod n)
可以写成 a e = 1 + k n ⇒ a e − k n = 1 ae = 1+kn \Rightarrow ae-kn=1 ae=1+knaekn=1
由于k是系数,令x=e,y=-k,b=n所以上式可以写成 a x + b y = 1 ax+by=1 ax+by=1
这就找出了题目与模乘逆元的关系,我们要求x,就是求a的逆元。
又回到了求逆元=-=+
a x + b y = 1 = g c d ( a , b ) = g c d ( b , a   m o d   b ) ax+by=1=gcd(a,b)=gcd(b,a\ mod\ b) ax+by=1=gcd(a,b)=gcd(b,a mod b)
= b x ′ + ( a − [ a b ] b ) y ′ =bx'+(a-[\frac{a}{b}]b)y' =bx+(a[ba]b)y([]是向0取整)
= a y ′ + b ( x ′ − [ a b ] y ′ ) =ay'+b(x'-[\frac{a}{b}]y') =ay+b(x[ba]y)
所以可以得到
{ x = y ′ y = x ′ − [ a b ] y ′ \begin{cases} x=y' \\ y=x'-[\frac{a}{b}]y' \end{cases} {x=yy=x[ba]y
利用这个公式,就可以进行递推。
由欧几里得定理可知,a和b辗转相除到最后一步,a = gcd(a,b),b = 0
而这里gcd(a,b) = 1,所以递推到最后一步时,a = 1,b = 0,有一组整数解x = 1,y = 0使ax+by=1成立(因为b = 0,所以y可以取任何数,只需要最后算出的x再用x = (x%b+b)%b能得出结果,为什么要这一步,请看下文),然后以x=1,y=0为基础递归,通过上面的递归公式就可以得到答案。但是得到的x可能并不是最小正整数,还要让
x = (x%b+b)%b.(为什么要有这一步?)
x是a(mod b)的逆元, a x ≡ 1 ( m o d   b ) ax\equiv1(mod\ b) ax1(mod b)
a x ( m o d   b ) = a ( m o d   b ) ∗ x ( m o d   b ) ax(mod\ b) = a(mod\ b) * x(mod\ b) ax(mod b)=a(mod b)x(mod b)
我们已经求出了一个x,但是这个x可能不是最小正整数解(可能是负数或者更大的数),所以我们让x%b使x接近b(可能还是负数,所以这里用接近这个词)再加上b保证它成为正数,但又可能不是最小,所以再%b。
举个栗子:
给定a = 10,b = 3.
10 x + 3 y = 1 10x+3y=1 10x+3y=1 (a = 10,b = 3)
⇒ 3 x + y = 1 \Rightarrow 3x+y = 1 3x+y=1 (a = 3,b = 10%3=1)
⇒ x = 1 \Rightarrow x = 1 x=1 (a = 1,b = 3%1=0)
然后递归
x = 1 , y = 0 x = 1,y = 0 x=1,y=0
⇒ x = 0 , y = 1 − [ 3 1 ] ∗ 0 = 1 \Rightarrow x = 0,y = 1-[\frac{3}{1}]*0 = 1 x=0,y=1[13]0=1
⇒ x = 1 , y = 0 − [ 10 3 ] ∗ 1 = − 3 \Rightarrow x = 1,y = 0 -[\frac{10}{3}]*1=-3 x=1,y=0[310]1=3
最后 x = ( 1 % 3 + 3 ) % 3 = 1 x = (1 \%3 +3)\%3 = 1 x=(1%3+3)%3=1所以答案就是1;
代码写法:

void ko(int a,int b,int &x,int &y)
{
	if(!b)
	{
		x = 1;
		y = 0;//可以为任何数
		return;
	}
	ko(b,a%b);
	int tem = x;
	x = y;
	y = tem - a/b*y;
}

费马小定理

假如a式一个整数,p是一个质数,那么 a p − a a^p-a apa是p的倍数,也可以表示为 a p ≡ a ( m o d   p ) a^p\equiv a(mod\ p) apa(mod p).如果a与p互质的话,可以改写成 a p − 1 ≡ 1 ( m o d   p ) a^{p-1}\equiv1(mod\ p) ap11(mod p)

费马小定理的证明

( n + 1 ) p = n p + C p 1 n p − 1 + . . . + C p p − 1 n + 1 (n+1)^p = n^p+C^{1}_{p}n^{p-1}+...+C^{p-1}_{p}n+1 (n+1)p=np+Cp1np1+...+Cpp1n+1
⇒ ( n + 1 ) p − n p − 1 ≡ 0 ( m o d   p ) \Rightarrow (n+1)^p-n^p-1\equiv0(mod\ p) (n+1)pnp10(mod p)
⇒ ( n + 1 ) p − 1 ≡ n p ( m o d   p ) \Rightarrow(n+1)^p-1\equiv n^p(mod\ p) (n+1)p1np(mod p)
⇒ ( n + 1 ) p − ( n + 1 ) ≡ n p − n ( m o d   p ) \Rightarrow(n+1)^p-(n+1)\equiv n^p-n(mod\ p) (n+1)p(n+1)npn(mod p)
通过数学归纳法证明:
当p = 1时, 0 ≡ 0 ( m o d   1 ) 0\equiv0(mod\ 1) 00(mod 1)成立,
假设p=k时, n k − n ≡ 0 ( m o d   p ) n^k-n\equiv 0(mod\ p) nkn0(mod p)成立
当p = k+1时, ( n + 1 ) p − ( n + 1 ) ≡ n p − n ( m o d   p ) ≡ 0 ( m o d   p ) (n+1)^p-(n+1)\equiv n^p-n(mod\ p)\equiv 0(mod\ p) (n+1)p(n+1)npn(mod p)0(mod p)
即证: a p − a ≡ 0 ( m o d   p ) a^p-a\equiv0(mod\ p) apa0(mod p)

费马小定理求逆元

通过费马小定理我们知道: a p − 1 ≡ 0 ( m o d   p ) a^{p-1}\equiv0(mod\ p) ap10(mod p)
⇒ a ∗ a p − 2 ≡ 0 ( m o d   p ) \Rightarrow a*a^{p-2}\equiv0(mod\ p) aap20(mod p)
所以,a的逆元就是 a p − 2 a^{p-2} ap2


上例题!

给定质数p和正整数a,求满足 a x ≡ 1 ( m o d   p ) ax\equiv1(mod\ p) ax1(mod p)的最小正整数x,如果不存在则返回-1

和第一个例题十分相似,唯一不同的就是例题1中b可以不是质数,而该例题中p为质数。
同样,如果a和p不互质的话,返回-1;
如果a和p互质,那么我们只需要求出 a p − 2 a^{p-2} ap2即可
为了求出 a p − 2 a^{p-2} ap2,由于p-2可能很大,导致时间复杂度很高
我们需要引入新的算法“快速幂”来降低时间复杂度。

快速幂

先举个栗子·-·
a ( 22 ) 10 = a ( 10110 ) 2 = a 16 ∗ a 4 ∗ a 2 a^{{(22)}_{10}} = a^{{(10110)}_{2}} = a^{16}*a^{4}*a^2 a(22)10=a(10110)2=a16a4a2将原本的22次乘法缩短为3次乘法。
代码实现:

int quickmi(int a,int b)
{
	int res = 1;
	while(b)
	{
		//如果b当前该位为1,那么就要乘上a。
		if(b & 1)res = res * a % MOD;//(一般都会要模一个数的)
		//a是随着b的位移而平方。(即a的上标分别对应1 2 4 8 ...)
		a = a * a % MOD;
		b>>=1;//b向右位移
	}
	return res;
}

但是!,凡事都有意外,两数相乘时可能爆int,把
res = res * a % MOD;改成res = (long long)res * a %MOD可能也会爆long long
这个时候我们就需要一些小技巧了。

优化模数为64为整数技巧(我也不知道叫什么名字=-=、)

我们可以借鉴二进制快速幂的思想,把a * b中的b写成2进制的形式
举个栗子·-·
a ∗ ( 22 ) 10 % M O D = a ∗ ( 10110 ) 2 % M O D = ( a ∗ 16 % M O D + a ∗ 4 % M O D + a ∗ 2 % M O D ) % M O D a*(22)_{10}\%MOD = a*(10110)_2\%MOD=(a*16\%MOD+a*4\%MOD+a*2\%MOD)\%MOD a(22)10%MOD=a(10110)2%MOD=(a16%MOD+a4%MOD+a2%MOD)%MOD
这样就可以将一个大数拆分成很多个小数取模之后相加取模,这样可以避免爆数据结构的情况。
代码实现:

int goodmul(int a,int b)
{
	int res = 0;//因为是加法,初始值为0;
	while(b)
	{
		if(b & 1)res = (res + a)%MOD;
		a = (a + a)%MOD
		b>>=1;
	}
	return res;
}

然后快速幂的代码就可以改写为

int quickmi(int a,int b)
{
	int res = 1;
	while(b)
	{
		//如果b当前该位为1,那么就要乘上a。
		if(b & 1)res = goodmul(a,res);//(一般都会要模一个数的)
		//a是随着b的位移而平方。(即a的上标分别对应1 2 4 8 ...)
		a = goolmul(a,a);
		b>>=1;//b向右位移
	}
	return res;
}

ok,了解了快速幂与大数模乘优化之后,回到费马小定理求模乘逆元
直接调用quickmi(a,p-2)计算即可。


逆元有许多种运用,目前我所接触到的就是组合数与逆元
C n m = n ! ( n − m ) ! ( m ) ! C^{m}_{n} = \frac{n!}{(n-m)!(m)!} Cnm=(nm)!(m)!n!,通常先求出阶乘,再相除,但是通常阶乘会很大导致爆int,这时我们就要用到逆元了。
组合数还可以写成
C n m = n ! ( n − m ) ! − 1 m ! − 1 C^{m}_{n} = n!(n-m)!^{-1}m!^{-1} Cnm=n!(nm)!1m!1,其中 ( n − m ) ! − 1 (n-m)!^{-1} (nm)!1 m ! − 1 m!^{-1} m!1就分别是 ( n − m ) ! (n-m)! (nm)! m ! m! m!的逆元,通过逆元把除法变成乘法,就可以使用一开始证明的公式直接模乘,求出组合数啦!

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值