乘法逆元.

乘法逆元

  前置知识:扩展欧几里得算法

定义:

乘法逆元,是指数学领域群G中任意一个元素a,都在G中有唯一的逆元a‘,具有性质a×a’=a’×a=e,其中e为该群的单位元

(以上内容来自百度)

  我们要考虑的是模 m 乘法逆元:若整数 b,m 互质,并且 b ∣ a b \mid a ba,则存在一个整数 x,使得 a x ≡ a / b ( m o d p ) ax \equiv a/b \pmod p axa/b(modp)。我们就称 x 为 b 的模 m 乘法逆元。记做 b − 1 b^{-1} b1


求解乘法逆元

  因为: a / b ≡ a b − 1 ≡ a × b / b × b − 1 ( m o d m ) a/b \equiv ab^{-1} \equiv a \times b/b \times b^{-1} \pmod m a/bab1a×b/b×b1(modm)
  所以: b × b − 1 ≡ 1 ( m o d m ) b\times b^{-1} \equiv 1 \pmod m b×b11(modm)
  如果 m 是质数(后文用 p 代替),并且 b < p,根据欧拉定理( b ϕ ( p ) ≡ 1 ( m o d p ) b^{\phi(p)} \equiv 1 \pmod p bϕ(p)1(modp))我们知道 b p − 1 ≡ 1 ( m o d p ) b^{p-1} \equiv 1 \pmod p bp11(modp),也就是说 b × b p − 2 ≡ 1 ( m o d p ) b\times b^{p-2} \equiv 1 \pmod p b×bp21(modp)。因此,当模数 p 为质数时, b p − 2 b^{p-2} bp2 就是 b 的模 p 乘法逆元

  但是对于更一般的情况就只能通过求解一次同余方程来求得乘法逆元了。下面我们来说线性同余方程的解法:
  对于这个方程: a x ≡ b ( m o d m ) ax \equiv b \pmod m axb(modm),它等价于 m ∣ ( a x − b ) m \mid (ax-b) m(axb)。我们令 ax - b = -ym。则有 ax + my = b。根据在扩展欧几里得算法里面讲过的 B e ˊ z o u t B\acute{e}zout Beˊzout 定理及其推论,当且仅当 g c d ( a , m ) ∣ b gcd(a, m) \mid b gcd(a,m)b 时,方程有解。在有解时,我们先用 exgcd 求出ax + my = gcd(a, m) 的一组特解 x 0 , y 0 x_0, y_0 x0,y0。然后 x = x 0 b g c d ( a , m ) x = x_0 \frac{b}{gcd(a, m)} x=x0gcd(a,m)b就是原方程的一个解了。
  在求解乘法逆元时就是求解方程: a x ≡ 1 ( m o d b ) ax \equiv 1\pmod b ax1(modb)时,先改写方程为 a x + b y = 1 ax + by = 1 ax+by=1,所以我们知道方程当且仅当 a, b 互质时有解。先用 exgcd 得求出方程的一组特解 x 0 , y 0 x_0, y_0 x0,y0。则 x 0 x_0 x0 就是原方程的一个解。
  通解也很好想出来就是模 b 与 x 0 x_0 x0 同余的所有整数。所以我们如果想要 x 的最小整数解,我们就可以利用取模运算把 x 0 x_0 x0 移动到 [1, b) 的区间内: x 0 = ( ( x 0    m o d    b ) + b )    m o d    b x_0 = ((x_0 \;mod \; b) + b) \; mod \;b x0=((x0modb)+b)modb

#include<bits/stdc++.h>
using namespace std;

int a = 0, b = 0;

 1. List item

int exgcd(int a, int b, int &x, int &y){        // ax + by = gcd(a, b) --> (x, y)
	if(b == 0){
		x = 1; y = 0;
		return a;
	}
	int d = exgcd(b, a % b, x, y);
	int z = x; 
	x = y; y = z - (a / b) * y;
	return d;
}

int inver(int a, int b){                        // ax ≡ 1 (mod b) --> x
	int x = 0; int y = 0;
	exgcd(a, b, x, y);
	return (x % b + b) % b;
}

int main(){
	scanf("%d%d", &a, &b);
	printf("%d\n", inver(a, b));
	return 0;
}


另一种方法求逆元–递推

  扩展欧几里得常用于求解单个数的逆元,而如果我们需要一堆数的逆元的话,我们一般就是用递推的方法进行求解。推导过程如下:
  设:
i k + r = p → i k + r ≡ 0 ( m o d p ) ik + r = p \rightarrow ik + r \equiv 0 \pmod p ik+r=pik+r0(modp)

  两边同时乘上 i − 1 r − 1 i^{-1}r^{-1} i1r1 得到:
i k × i − 1 r − 1 + r × i − 1 r − 1 ≡ 0 ( m o d p ) → k r − 1 + i − 1 ( m o d p ) ik\times i^{-1}r^{-1} + r\times i^{-1}r^{-1} \equiv 0 \pmod p \rightarrow kr^{-1} + i^{-1} \pmod p ik×i1r1+r×i1r10(modp)kr1+i1(modp)
  所以:
i − 1 ≡ − k r − 1 ( m o d p ) i^{-1} \equiv -kr^{-1} \pmod p i1kr1(modp)
  因为我们设的是 i k + r = p ik + r = p ik+r=p,所以 r ∈ ( 0 , i ) r \in (0, i) r(0,i),r < i。所以在递推的过程中, r − 1 r^{-1} r1 肯定在之前就被求出来了。所以我们就可以由此确定 i − 1 i^{-1} i1。下面证明递推式:
  因为 i k + r = p ik + r = p ik+r=p 所以 k = ⌊ p i ⌋ , r = p m o d    i k = \lfloor\frac{p}{i}\rfloor,r = p\mod i k=ipr=pmodi,所以:
i − 1 ≡ − ⌊ p i ⌋ × ( p    m o d    i ) − 1 ( m o d p ) i^{-1} \equiv - \lfloor \frac{p}{i} \rfloor \times (p \; mod \; i)^{-1} \pmod p i1ip×(pmodi)1(modp)
  然后我们就可以得到递推式:
i − 1 = − ⌊ p i ⌋ × ( p    m o d    i ) i^{-1} = - \lfloor \frac{p}{i} \rfloor \times (p \; mod \; i) i1=ip×(pmodi)
  如果我们要得到最小整数解,我们可以在乘法的第一项加上一个 p,就是这样:
i − 1 = ( p − ⌊ p i ⌋ ) × ( p    m o d    i ) i^{-1} = (p - \lfloor \frac{p}{i} \rfloor) \times (p \; mod \; i) i1=(pip)×(pmodi)
  代码如下:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100100

int n = 0; int p = 0;
int inv[MAXN] = { 0 };

int main(){
	scanf("%d%d", &n, &p);
	inv[1] = 1;
	for(int i = 2; i <= n; i++){
		inv[i] = (p - p / i) * inv[p % i] % p;
		printf("%d ", inv[i]);
	}
	return 0;
}

参考书籍:《算法竞赛进阶指南》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值