求逆元方法总结
来源:互联网 发布:手机淘宝怎样朋友代付 编辑:程序博客网 时间:2024/05/22 17:34
http://blog.csdn.net/hlyfalsy/article/details/38067021
在MOD的情况下, (a*b/c ) %MOD 不能直接 / c 来求,需要找到一个数 inv 使得 inv * c % MOD = 1 。 这样 (a*b / c) % MOD = (a * b * inv) % MOD;
性质: 逆元是积性函数 存在 a*b = c ,那么 inv[c] = inv[a] * inv[b] % MOD;
1、 循环找解的方法
- long long circleRun(long long n){
- for(long long i = 1;i < MOD;i++)
- if(i * n % MOD == 1)
- return i;
- return -1;
- }
- long long n;
- int main(){
- while(cin >> n){
- cout << n << " 's inv is "<<endl;
- cout << circleRun(n) << endl;
- }
- return 0;
- }
2、费马小定理的解法 MOD 是质数才能用
利用 a ^ (p-1) % MOD === 1 , 那么它的逆元就是 a ^ (p-2)
- #include<stdio.h>
- #include<string.h>
- #include<iostream>
- #include<cmath>
- using namespace std;
- const int MOD = 1e9+7;
- long long quickpow(long long base,long long n){
- long long ans = 1;
- while(n){
- if(n%2 == 1) ans = ans * base % MOD;
- n /= 2;
- base = base * base % MOD;
- }
- return ans;
- }
- long long n;
- int main(){
- while(cin >> n){
- cout << n << " 's inv is "<<endl;
- //cout << circleRun(n) << endl;
- cout << " a ^ (p-2) % MOD "<< endl;
- cout << quickpow(n, MOD-2) << endl;
- }
- return 0;
- }
3、利用欧几里德扩展来求 ,
欧几里德扩展 是用来解决 ax + by = gcd(a,b)这样的等式。
这时候取 b = MOD, 你可以写成这样 ax = gcd(a,b) - by
推导出 a*x % MOD = gcd(a,b) %MOD
所以只要 gcd(a,b) % MOD === 1时,就可以使用这条来求a的逆元
但用exgcd求得时候,inv可能是负数, 还需要进行如下操作
- inv = (inv % MOD + MOD) % MOD;
- long long exGcd(long long a, long long b, long long &x0, long long &y0) // a*x0 + b*y0 = gcd(a,b)
- {
- if(b==0)
- {
- x0 = 1;
- y0 = 0;
- return a;
- }
- long long r = exGcd(b, a % b, x0, y0);
- long long t = x0;
- x0 = y0;
- y0 = t - a / b * y0;
- return r;
- }
- long long n;
- int main(){
- while(cin >> n){
- cout << n << " 's inv is "<<endl;
- //cout << circleRun(n) << endl;
- cout << " a ^ (p-2) % MOD "<< endl;
- cout << quickpow(n, MOD-2) << endl;
- cout << " ax + by = gcd(a,b) " << endl;
- long long inv,y0;
- exGcd(n ,MOD,inv,y0);
- inv = (inv % MOD + MOD) % MOD;
- cout << inv << endl;
- }
- return 0;
- }
预处理1-n关于p的逆元:(n < p) , 因为 逆元是积性函数,所以只要 p > n 成立即可,而不需要p必须为素数
假设已经预处理了1-i-1的逆元,j的逆元设为F[j]
令p = x * i –y ( 0 < y < i)
X* i = y (mod p)
X* F[y] * i = y * F[y] = 1(mod p)
所以i的逆元是F[i] = X* F[y]
这样就可以O(n)的时间预处理了。
参考链接
代码实现
- const int N = 100005;
- long long _inv[N];
- void pre() {
- _inv[0] = _inv[1] = 1;
- for (int i = 2; i < N; i++) {
- _inv[i] = ((MOD - MOD / i) * _inv[MOD % i]) % MOD;
- }
- }
- long long n;
- int main(){
- pre();
- while(cin >> n){
- cout << _inv[n] << endl;
- }
- return 0;
- }
4、 利用逆元函数是完全积性函数的性质
求所有质数的逆元即可,预处理复杂度是O(n / logn * logn) = O(n)
这种写法可以用 exgcd 来实现素数的logn 求逆,因为此时 a = p , MOD无论取何值(p除外) , 都有 gcd(p ,mod) = 1,适合通用情况。
之后再采取质因数分解的方法,即可对任意一个 n 以 logn速度求出其逆元
不过。。ACM竞赛如果为了求逆写这么长的代码貌似不太现实。
- 求逆元方法总结
- 求逆元方法总结
- 各种求逆元方法总结
- 求逆元的一些方法总结
- 方法总结
- 方法总结
- 方法总结
- 方法总结
- 方法总结
- 英语听力方法总结(转载)
- 构造TPtr方法总结
- 数据库连接方法总结
- VC 贴图方法总结
- A*方法总结
- EJB方法总结
- asp注入方法总结
- jdbc 数据库连接方法总结
- Debug方法总结
- hdu 4902 线段树双标记类型题
- Copy List with Random Pointer
- Android SDK离线安装方法详解(加速安装)
- 使用autotools生成Makefile
- 黑马程序员 java学习笔记——javaBean
- 求逆元方法总结
- linux基础命令
- EditText属性简介
- DM6437外设
- PAT 1082. Read Number in Chinese (25)
- AJAX传递数组到后台处理
- C#代码修改图片分辨率大小
- Activity状态的保存和保持( onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance )
- Create Linux Loopback File System On Disk File