📂数学
🔖逆元
2022-07-20 14:07阅读: 4635评论: 0推荐: 2

乘法逆元 | 分数 (小数) 取模

作用

比赛中大多数情况为了避免大整数运算,大多都会要求答案对一个数(大多是质数)取模。

  1. 题目中用到除法
    因为ab%p(a%pb%p)%p ,此时就无法计算,需要用到乘法逆元

  2. 当题目遇到小数
    因为取模运算时对于整数来说的,所以无法计算,需要用到乘法逆元。
    虽然c语言有fmod函数,但是函数fmod后结果还是小数,如果题目要求mod后为整数,还需要用到逆元。

定义

逆元素(简称逆元) 是指一个可以取消另一给定元素运算的元素,如加法中的加法逆元(相反数)和乘法中的倒数。简单来说,A的逆元就是能抵消A的影响的元素。

如果说 a 在模 p 意义下的乘法逆元是 x,
那么 ax1(modp)
(ax模p等于1模p)

不难发现,如同 0 没有倒数一样,0没有乘法逆元
a 在模 p 意义下的乘法逆元均为 [1,p−1] 中的整数。

然后,因为求模意义下的a/b,就是求bca(modp)的c,
这时给a乘上b的逆元就消去了乘b的影响,结果为c,即

bc×b1a×b1(modp)

ca×b1(modp)

模意义下除以一个数就等价于乘这个数的乘法逆元

性质

  1. 存在唯一性:对 a 来说,若它有逆元,则在p范围内一定只有一个逆元。

  2. a 在模 p 意义下的乘法逆元存在,当且仅当 a,p 互质。

这也在一定程度上解释了大多数题目模数为什么为质数的原因:(设模数为 p)可以保证在[1,p1] 中的所有整数都有模 p 意义下的逆元。

求逆元

快速幂求逆元

对于ax1(modp)求x
因为: a 在模 p 意义下的乘法逆元存在,当且仅当 a,p 互质。
有费马定理得到:

ap11(modp)

那么

a×ap21(modp)

那么a的逆元就是:ap2modp

代码:

int qmi(int a, int k, int p)
{
int res = 1;
while(k)
{
if(k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
int get(int a,int p){
if(a % p == 0) return -1;
else return qmi(a,p-2,p);
}

拓展欧几里得求逆元

补充:下面3个公式都成立
image

不难发现ax1(modp)等价于ax+py=1

点击查看证明

将同余号换为等号得:

axmodp=1modp

移项得:

(ax1)modp=0

由上式可知ax1为 p 的整数倍,不妨设其为 p的k倍(kZ)

ax1=kp

继续移项得:

axkp=1

y=k就写成了上文的形式:

ax+py=1

又因为乘法逆元存在,当且仅当 a,p 互质,即gcd(a,p)=1

故我们可以直接运用拓展欧几里得算法解 ax+py=gcd(a,p)这个方程。

时间复杂度: log(p)
手算模拟:
image

整个求解的过程就是使用辗转相除法求两个数的公约数,并且我们知道公约数的结果为1,所以一直计算到1为止即可。
例1:求在p = 14的条件下 a = 5的逆元
等号左边为余数

4=1452

1=541

等号左侧得到1,进行等式代换:

1=5(1452)1

1=5141+52

1=53141

得到结果为3
例2:求在p = 18的条件下 a = 5的逆元

前面的步骤相同,使用辗转相除,会得到3个式子:

3=1853

2=531

1=321

在代换时会发现一个问题,由于式子是奇数个,所以最后整理时a的系数为负:

1=3(531)1

1=(1853)(5(1853)1)1

1=18535+1853

1=18257

此时将结果表示出来会是这个样子:

15(7)mod18

但是利用两个数互质的性质以及最小公倍数,我们可以直接得到想要的结果:

15(7)mod185(11)mod18

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
long long a, b;
//b是p,x是逆元
int exgcd(long long a, long long b, long long &x, long long &y)
{
if (b == 0)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
scanf("%lld%lld", &a, &b);
long long x = 0, y = 0;
exgcd(a, b, x, y);
printf("%lld", (x % b + b) % b);//这里防止出现负数
return 0;
}

原文 1
原文 2

原文 3

本文作者:kingwzun

本文链接:https://www.cnblogs.com/kingwz/p/16497759.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   kingwzun  阅读(4635)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起