数论倒数(逆元)——数论

数论倒数(又称逆元):

先来引入求余概念 :

(a + b) % p = (a%p + b%p) %p (对)

(a - b) % p = (a%p - b%p) %p (对)

(a * b) % p = (a%p * b%p) %p (对)

(a / b) % p = (a%p / b%p) %p (错)

除法的是错误的,举一个例子就可以明白了:(6/2)%4=3,(6%4)/(2%4)=2/2=1
所以,(a / b) % p = (a%p / b%p) %p 是错的。
那么,当遇到在求余过程中遇到除法怎么办???这就体现出逆元的重要性了。
如果 ax = 1;那么 x 是 a 的倒数, x = 1/a;
但是a如果不是1,那么x就是小数。
那数论中,大部分情况都有求余,所以现在问题变了
a
x = 1 (mod p)
那么x一定等于1/a吗???
当然不一定!!!!!
所以这时候,就把x看成a的倒数,只不过加了一个求余条件,所以x叫做: a关于p的逆元。
比如2 * 3 % 5 = 1,那么3就是2关于5的逆元,或者说2和3关于5互为逆元
这里3的效果是不是跟1/2的效果一样,所以才叫数论倒数(逆元)
a的逆元,用 inv(a)来表示
那么 (a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p
这样就把除法,完全转换为乘法了

下面介绍几种逆元的求法

通过扩展欧几里得算法求逆元:

// 求解得到的 x 就是 a 的逆元,y 就是 b 的逆元。
int exgcd(int a, int b, int &x, int &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int r = exgcd(b, a % b, x, y);
    int t = x;
    x = y;
    y = t - a / b * y;

    return r;
}
/*求解ax+by=gcd(a,b),亦即ax≡1(mod b)。函数返回值是a,b的最大公约数,而x即a的逆元。
注意a, b不能写反了。*/

帮你理解
假设有方程 ax + by = 1。由exgcd我们知道,如果ab互质,则方程有解。
那么这个解出来的x就是a关于b的逆元,y就是b关于a的逆元。
给方程两边同时对b求余:
a * x % b + b * y % b = 1 % b   (b * y % b =0)
变成  a*x % b = 1 % b
转换个表示形式  a * x ≡ 1(mod b)

此方法求逆元时,一定要符合要求,即:ax + by = 1 且 gcd ( a, b) = 1 (a, b 互质),这样带入函数求出的结果才对应

所以x就是a关于b的逆元,同理可证y。

通过快速幂求逆元:

// 要求 mod 为质数
long long quick_pow(long long a,long long b, long long mod)    // a 为底,b 为指数
{
    long long ans = 1;                          // ans 存储结果
    long long base = a % mod;                   // base 为底
    while(b != 0)                                            
    {
        if(b & 1)                                 //如果 b 为奇数
            ans = ans * base % mod;              //先乘以一个底,把指数变成偶数
        base = base * base % mod;               //指数消去 2 
        b >>= 1;                                  //指数向右移动一位,即除以2
    }
    return ans;
}
long long fermat (long long a, long long mod)
{
    return quick_pow(a, mod - 2, mod);
}

费马小定理a^(p-1) ≡1 (mod p)这里 p 要求是一个质数,原始式子是 a^p ≡ a (mod p)
两边同除以a,得到:
a ^ (p - 2) ≡ 1 / a (mod p)
把1 / a 表示成逆元:a ^ (p-2) ≡ inv(a) (mod p)
得到:inv(a) = a ^ (p-2) (mod p)
用快速幂求一下,复杂度O(logn)。

通过递推求逆元:

// 求 n 以内所有数的逆元,但是 p 必须是奇质数.
int inv[N];
void get_inverse(int n, int p) {
	inv[1] = 1;
	for (int i = 2; i <= n; ++i) {
		inv[i] = (p - p / i) * inv[p % i] % p;
	}
}

这个算法可以在O(n)的时间复杂度内求出 n 以内所有数的逆元,不过需要注意的是p必须为奇质数。
这是网上的推导,解释起来有点麻烦。先用用就行了吧!!以后再慢慢理解。
数论倒数(逆元)——数论_第1张图片
最后,这里有一个求解逆元方法总结的网站:https://syncshinee.github.io/2015/05/10/InverseElement/

你可能感兴趣的:(数论)