zoukankan      html  css  js  c++  java
  • (转载)各种求逆元方法总结

    在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;
    }

    4、利用某神奇推导。。 O(n)求出 1---- n 的所有逆元。 

    预处理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竞赛如果为了求逆写这么长的代码貌似不太现实。

  • 相关阅读:
    MySQL-基本sql命令
    Java for LeetCode 203 Remove Linked List Elements
    Java for LeetCode 202 Happy Number
    Java for LeetCode 201 Bitwise AND of Numbers Range
    Java for LeetCode 200 Number of Islands
    Java for LeetCode 199 Binary Tree Right Side View
    Java for LeetCode 198 House Robber
    Java for LeetCode 191 Number of 1 Bits
    Java for LeetCode 190 Reverse Bits
    Java for LeetCode 189 Rotate Array
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/3973467.html
Copyright © 2011-2022 走看看