zoukankan      html  css  js  c++  java
  • 乘法逆元数论篇

    乘法逆元数论篇【易懂教学】

    引入篇

    乘法逆元较多用于求解除法取模问题
    例如:(a/b)%m时,可以将其转换为(a%(b×m))/b,但这样求解的过程依然涉及到除法,所以我们应当避免除法的直接计算。这时候就需要用到我们要讲的乘法逆元。
    可以使用逆元将除法转换为乘法:假设b存在乘法逆元,即与m互质(充要条件)。

    设c是b的逆元,即 b×c≡1(mod m)
    那么有 a/b=(a/b)×1=(a/b)×b×c=a×c(mod m)
    即除以一个数取模等于乘以这个数的逆元取模

    但是在讲乘法逆元之前就必须要引入一个小的知识点:同余式

    如果两个正整数a和b之差能被n整除,那么我们就说a和b对模n同余,记作:a≡b (mod n)。
    a≡b(mod n)等价于a与b分别用n去除,余数相同。

    算法篇

    求乘法逆元的算法有好几种,比如:扩展欧几里得、费马小定理、欧拉函数。下面是这几种算法适用的情况:

    • 逆元求解一般利用扩欧。
    • 当m为质数的时候直接使用费马小定理
    • m非质数使用欧拉函数。

    扩展欧几里得算法

    要求a,n互为素数,存在唯一解

    代码部分

    int extgcd(int a, int b, int& x, int& y)
    {
        int d = a;
        if(b != 0)
        {
            d = extgcd(b, a % b, y, x);
            y -= (a / b) * x;
        }
        else
        {
            x = 1;
            y = 0;
        }
        return d;
    }
    int mod_inverse(int a, int n)
    {
        int x, y;
        extgcd(a, n, x, y);
        return (n + x % n) % n;
    }
    ///mod_inverse为对n取模a的逆元

    费马小定理

    在n为素数的情况下,对任意的a都有a^n≡a(mod n)
    如果a无法被n整除,则有a^(n-1)≡1(mod n)
    可以在n为素数的情况下求出一个数的逆元,a×a^(n-2)≡1(mod n),这时候,a^(n-2)为a的逆元。

    代码部分

    ///这里用到了整数快速幂
    ll mult(ll a,ll n)  //求a^n%mod
    {
        ll s=1;
        while(n)
        {
            if(n&1)s=s*a%mod;
            a=a*a%mod;
            n>>=1;
        }
        return s;
    } //mult(a,n-2);

    欧拉函数

    简单介绍一下欧拉函数:在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(1)=1)。
    如果对欧拉函数有兴趣的可以看看:欧拉函数

    令φ(n)表示小于等于n且与n互素的正整数的个数。
    如果a和n互质,则有a^φ(n)≡1(mod n),即 a×a^(φ(n)−1)≡1(mod n),那么a^(φ(n)−1)为a的逆元。
    在n为质数的时候φ(n) = n - 1。

    代码部分

    ///欧拉函数
    int euler(int n)
    {
        int res = n;
        for(int i = 2; i * i <= n; i++)
        {
            if(n % i == 0)
            {
                res = res / i * (i - 1);
                while(n % i == 0) n /= i;
            }
        }
        if(n != 1) res = res / n * (n - 1);
        return res;
    } 
    ///求对mod = n,a的逆元
    ll mult(ll a,ll n) 
    {
    
        ll s=1;
        while(n)
        {
            if(n&1)s=s*a%mod;
            a=a*a%mod;
            n>>=1;
        }
        return s;
    } //mult(a, euler(n)-1);
    

    最后加上一个欧拉函数打标的函数

    int euler[maxn];
    void euler()
    {
        for(int i = 0; i < maxn; i++)  euler[i] = i;
        for(int i = 2; i < maxn; ++i)
        {
            if(euler[i] == i)
            {
                for(int j = i; j < maxn; j += i)
                {
                    euler[j] = euler[j] / i * (i - 1);
                }
            }
        }
    }
  • 相关阅读:
    题解——洛谷P3812【模板】线性基
    题解——洛谷P2781 传教(线段树)
    题解——洛谷P1962 斐波那契数列(矩阵乘法)
    题解——洛谷P3390 【模板】矩阵快速幂(矩阵乘法)
    题解——牛客网OI赛制测试赛2
    题解——code[vs] 1506 传话(传递闭包)
    题解——Codeforces Round #508 (Div. 2) T3 (贪心)
    题解——Codeforces Round #508 (Div. 2) T2 (构造)
    题解——Codeforces Round #508 (Div. 2) T1 (模拟)
    题解——Codeforces Round #507 (based on Olympiad of Metropolises) T2(模拟)
  • 原文地址:https://www.cnblogs.com/nanfenggu/p/7900049.html
Copyright © 2011-2022 走看看