逆元的三种解法(附详细证明)
时间:2022-05-08
本文章向大家介绍逆元的三种解法(附详细证明),主要内容包括什么是逆元?、逆元的基本解法、1.快速幂、2.扩展欧几里得、3.递推、总结、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
友情提示:
Latex加载稍慢,请耐心等待
什么是逆元?
若x满足
a*xequiv 1(mod p)
我们称x是a在mod p意义下的逆元
逆元的基本解法
https://loj.ac/problem/110
1.快速幂
当p为素数
根据费马小定理
a^{(p-1)}equiv 1(mod p)
{color{Green}a*a^{(p-2)}equiv 1(mod p) }
带入快速幂就好啦
时间复杂度:O(log_2^p)
1 #include<cstdio>
2 #define LL long long
3 using namespace std;
4 const LL MAXN=200000001;
5 LL n,mod;
6 LL fastpow(LL val,LL p)
7 {
8 LL base=1;
9 while(p)
10 {
11 if(p&1) base=(base*val)%mod;
12 val=(val*val)%mod;
13 p>>=1;
14 }
15 return base;
16 }
17 int main()
18 {
19 scanf("%lld%lld",&n,&mod);
20 for(LL i=1;i<=n;i++)
21 printf("%lldn",fastpow(i,mod-2)%mod);
22 return 0;
23 }
2.扩展欧几里得
对于a*xequiv 1(mod p)
他的另一种写法为
a*x+p*y=1(想一想,为什么)
扩展欧几里得,带入求解
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 using namespace std;
5 int n,mod;
6 inline int read()
7 {
8 char c=getchar();int flag=1,x=0;
9 while(c<'0'||c>'9') {if(c=='-') flag=-1;c=getchar();}
10 while(c>='0'&&c<='9') x=x*10+c-48,c=getchar(); return x*flag;
11 }
12 int x,y;
13 int gcd(int a,int b)
14 {
15 return b==0?a:gcd(b,a%b);
16 }
17 int exgcd(int a,int b,int &x,int &y)
18 {
19 if(b==0)
20 {
21 x=1,y=0;
22 return a;
23 }
24 int r=exgcd(b,a%b,x,y);
25 int tmp=x;x=y;y=tmp-(a/b)*y;
26 return r;
27 }
28 int main()
29 {
30 n=read(),mod=read();
31 for(int i=1;i<=n;i++)
32 {
33 int g=exgcd(i,mod,x,y);
34 while(x<0) x+=mod;
35 printf("%dn",x);
36 }
37 return 0;
38 }
时间复杂度:O(log_2^n)
3.递推
前两种方法常用来求单个逆元
对于逆元的需要量比较大的时候,我们可以使用递推的方法来求逆元
前提条件:$P$为素数
推导过程
设t=P/i k=P mod i
显然有
t*i+k equiv 0 (mod P)
k equiv -t*i(mod P)
两侧同除i*k,把t和k带入
inv[i] equiv -p/i*inv[p mod i] (mod p)
这里需要注意一个事情,
对于 amod p当a<0时,
应为(a+p) mod p
这样就可以把原式的mod p消掉,得
inv[i]=P-P/i*inv[Pmod i]
这样就可以进行线性的递推啦
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<cmath>
5 #define LL unsigned long long
6 using namespace std;
7 const LL MAXN=200000001;
8 inline LL read()
9 {
10 char c=getchar();LL flag=1,x=0;
11 while(c<'0'||c>'9') {if(c=='-') flag=-1;c=getchar();}
12 while(c>='0'&&c<='9') x=x*10+c-48,c=getchar(); return x*flag;
13 }
14 LL inv[MAXN];
15 LL n,p;
16 int main()
17 {
18 n=read(),p=read();
19 inv[1]=1;
20 printf("1n");
21 for(int i=2;i<=n;i++)
22 {
23 inv[i]=(p-p/i)*inv[p%i]%p;
24 printf("%dn",inv[i]);
25 }
26 return 0;
27 }
时间复杂度:O(n)
总结
在求多个数的逆元的时候,推荐使用递推算法
在求单个数的逆元的时候,推荐使用扩展欧几里得算法
因为扩展欧几里得算法不受模数的限制,而且自测运行效率比快速幂高不少
- 短网址(short URL)系统的原理及其实现
- 设计和实现一款轻量级的爬虫框架
- JavaScript实现模糊推荐的input框(类似搜索框)
- hadoop streaming编程小demo(python版)
- 一个scrapy框架的爬虫(爬取京东图书)
- mongodb生产环境(副本集模式)集群搭建配置
- ELK日志收集分析系统配置
- 【学术】如何在15分钟内建立一个深度学习模型?
- Elasticsearch(GEO)空间检索查询
- java spark-streaming接收TCP/Kafka数据
- hiveQL求差集
- hiveQL去重
- springboot高并发redis细粒度加锁(key粒度加锁)
- java使用spark/spark-sql处理schema数据
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法