#edu1002. 快速幂与逆元

快速幂与逆元

前置知识

基本的取模运算

加法:(a+b)%p=((a%p)+(b%p))%p(a + b) \% p = ((a\%p)+(b\%p))\%p

减法:(ab)%p=((a%p)(b%p))%p(a - b) \% p = ((a\%p)-(b\%p))\%p ,注意:为防止出现负数,再加pp(ab)%p=((a%p)(b%p)+p)%p(a - b) \% p = ((a\%p)-(b\%p)+p)\%p

乘法:(a×b)%p=((a%p)×(b%p))%p(a \times b) \% p = ((a\%p)\times(b\%p))\%p

注意除法取模,需要使用逆元,将除法转换为乘法。

  • 请利用带余除法进行证明

快速幂

已知 a,b,pa,b,p,求 abmodpa^b \bmod p

方法1:分治

b=q2+b%2b=q *2+b\%2 得到ab=aq×aq×ab%2a^b=a^q \times a^q \times a^{b\%2},其中 q=b/2q=\left \lfloor b/2 \right \rfloor,即 ab=ab/2×ab/2×ab%2a^b=a^{b/2} \times a^{b/2} \times a^{b\%2}

ab/2a^{b/2} 与原问题具有相同结构的子问题,可以递归求解。对应的时间复杂度为 T(n)=T(n/2)+O(1)T(n)=T(n/2)+O(1) ,得到 T(n)=O(log2n)T(n)=O(\log_2{n})

核心代码如下:

int p;
int pw(int a,int b)
{
	if(b==0)return 1;
	int x=pw(a,b/2);
	if(b&1)
		return (((long long)x*x)%p*(long long)a)%p;
	else return (long long)x*x%p;	
}

注意:int 型 乘 int 型会炸 int (溢出),所以需要类型转换成 long long.

方法2:倍增

举例说明,若 b=15b=15,转为二进制可得 15=(1111)215=(1111)_2

a15=a1111=a8a4a2a1a^{15}=a^{1111}=a^8*a^4 *a^2*a^1 ,发现是若干个 aa 翻倍相乘得到。

a13=a1101=a8a4a1a^{13}=a^{1101}=a^8*a^4 *a^1,当 bb 中对应二进制为 11,将 aa 翻倍的值乘入,否则丢弃。

设变量 base=abase=a ,翻倍一次(倍增)就是 base=basebasebase=base*base ,可以依次得到 a1,a2,a4,a8a^1,a^2,a^4,a^8\dots ,当 bb 中二进制对应位为 11 ,乘入结果。

核心代码:

int p;
int pw(int a,int b)
{
	int base=a,ans=1;
	if(b==0)return 1%p;
	while(b)
	{
		if(b&1)ans=(long long)ans*base%p;
		base=(long long)base*base%p;
		b>>=1;
	}
	return ans;
} 

逆元

当遇到 (a/b)%p(a/b)\%p 的情况,需要将除法转为乘法。

逆元定义:正整数 b,pb,p,如果有 bx1modpbx ≡ 1\mod p,则称 xx 的最小正整数解为 bbpp 的逆元。

(a/b)%p=a1/b%p=abx/b%p=ax%p(a/b)\%p = a*1/b \%p =a*bx/b \%p =a*x \%p

逆元求法很多,比如扩展欧几里得、费马小定义、欧拉定理等。当 pp 为质数时,费马小定义最为简单,转化为快速幂求解。

费马小定理:如果 pp 是一个质数,而整数 bb 不是 pp 的倍数,则有 b(p1)1(modp)b^{(p-1)}≡1(\mod p)

由费马小定理,可得 b0b \ne 0 在模 pp 下的逆元, bbp21(modp)b*b^{p-2}≡1(\mod p) ,bb 的逆元为 x=bp2modpx=b^{p-2} \mod p

pp 不是质数时,可以利用欧拉定理求逆元:

如果 gcd(a,p)=1gcd(a,p)=1, 则 aφ(p)1modpa^{\varphi(p)} ≡ 1 \mod p.

即求出 φ(p)\varphi(p) 后求出逆元。(可以看到费马小定理是欧拉定理的特殊情况)