逆元的三种解法(附详细证明)
2018-06-17 21:41:22来源:未知 阅读 ()
友情提示:
Latex加载稍慢,请耐心等待
什么是逆元?
若$x$满足
$a*x\equiv 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("%lld\n",fastpow(i,mod-2)%mod); 22 return 0; 23 }
2.扩展欧几里得
对于$a*x\equiv 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("%d\n",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)$
这里需要注意一个事情,
对于 $a\mod p$当$a<0$时,
应为$(a+p) \mod p$
这样就可以把原式的$\mod p$消掉,得
$inv[i]=P-P/i*inv[P\mod 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("1\n"); 21 for(int i=2;i<=n;i++) 22 { 23 inv[i]=(p-p/i)*inv[p%i]%p; 24 printf("%d\n",inv[i]); 25 } 26 return 0; 27 }
时间复杂度:$O(n)$
总结
在求多个数的逆元的时候,推荐使用递推算法
在求单个数的逆元的时候,推荐使用扩展欧几里得算法
因为扩展欧几里得算法不受模数的限制,而且自测运行效率比快速幂高不少
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
下一篇:洛谷P1722 矩阵 II
- DP大大大大大赏 2019-08-16
- C++中的三种继承方式 2019-05-24
- SICP——换零钱递归解法(树形递归) 2019-03-10
- 最短点对 2019-01-01
- QT多线程的使用 2018-08-17
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash