zoukankan      html  css  js  c++  java
  • 浅谈逆元及其求法

    内容部分参考于以下博客

    https://www.cnblogs.com/rir1715/p/7748054.html#commentform

    https://baijiahao.baidu.com/s?id=1609032096408414934&wfr=spider&for=pc

    用途:

    就博主现在的认知来说(毕竟是一个蒟蒻,如果有新的认识会更新的),逆元主要是用在除法取模的时候。首先,如果是乘法,那么有 a*b%mod  = (a%mod*b%mod),这是显而易见的一个结论,但当我们遇到除法取模时(一般是在组合数取模的时候),就不会这么容易了,因为(a/b%mod !=((a%mod/b%mod)),这个时候就需要我们的逆元登场了,至于为什么,请看下面逆元的定义。

    定义:

    简单来说,逆元其实就相当于初中学的倒数。

    我们知道,对于一个数a,我们可以定义一个数b,使得 a*b = 1,这样的话,当我们需要除以a时,就可以转化为乘b,这样就能把除法转化为乘法。

    同样,对于除a取模于mod的一个运算,我们依然可以定义一个数b,使得

    num/a)%mod = (num*b)mod,这样的一个数b,就可以称b为a在模mod下的一个逆元,有了逆元的话,我们就能将除法的取模运算转化为乘法的取模运算,从而来解决一些问题。

    在这里给出逆元定义的数学形式(假设模数是n)

    a * b ≡ 1 (mod n)

    最后,要注意一点,一个数a在模n的情况下是不一定有逆元的,a关于n的逆元存在当且仅当a 和 n 互质,一个数a在模n的情况下逆元也不一定唯一,但我们只需要求出一个就够的,因为他们在模 n 的条件下结果是相同的。

    求法:

    目前,求解逆元一共有三种方法,接下来将一一讲解。

    整数为 a,模数为 p,a在mod p条件下的逆元为 x。

    1. 费马小定理

    应用范围:p为素数。

    这应该是最常用的的求逆元的方法,虽然有所局限,但确实是使用频率最高的一种算法了。

    原理:

    首先,我们由费马小定理得

       a^(p - 1) ≡ 1 mod p

    变形得

      a * a^(p-2) ≡ 1 mod p

    对比于逆元的定义

      a * x ≡ 1 mod p

    显然 我们可以得到

      x  ≡ a^(p-2) mod p

    这样,我们就由费马小定理推理出 a 在 mod p 条件下的逆元为 a^(p-2)。

    代码实现:

    typedef long long LL
    LL quick_pow(LL x, LL n LL p)// 快速幂求x^n mod p 的结果 { if(n==0) return 1; if(n==1) return x%p; LL ans = 1; LL tmp = x%p; while(n) { if(n&1) { ans = (ans*tmp)%p; } tmp = tmp*tmp%p; b>>=1; } return ans%p; } LL inv(LL b,LL p) //求数 b mod p 的逆元 { return quick_pow(b,p-2,p); }

    2. 拓展欧几里得

    局限:难以理解?(手动狗头)

    原理:

    敲起来太麻烦了,手写了

     

    代码实现:

    typedef long long LL
    LL ex_gcd(int a,int b,int &x,int &y)//注意x,y的传引用
    {
        if(a==0&& b==0)
            return -1;
        if(b==0)//递归终止条件
        {
            x = 1;
            y = 0;
            return a;
        }
        int ans  = ex_gcd(b,a%b,x,y);
        int t = x; // 扩欧方程的解
            x = y;
            y = x - a/b*y;
        return ans;
    }
    LL inv(LL b,LL p) //求数 b mod p 的逆元
    {
        int x,y;
        int d = ex_gcd(b,p,x,y);
        if(d==-1) // b,p不互素,利用扩欧求不出逆元
            return - 1else
         return (x+p)%p; //x 的值就是 逆元,加上p再取模消除负数的情况
    }

    3. 线性推

    原理:

     

    因此我们可以通过一个递推式在线性时间内推导出1——(p-1)mod p 的逆元,递推式如下注意在式子右边加上p来消除负号

      inv[i]=(p-p/i)*inv[p%i]%p;

    代码实现:

    typedef long long LL
    LL inv_arr[maxn];
    void inv(LL b,LL p) //可以得到1到b mod p 的逆元 (1 < b < p)
    {
        inv_arr[1] = 1;
        for(int i = 2;i<=b;i++)
            inv_arr[i] = (p-p/i)*inv_arr[p%i]%p;
        return;
    }

    如果还有其他疑问,欢迎在评论区留言

  • 相关阅读:
    Centos 环境变量
    Centos 多线程下载工具-axel
    【Sprint3冲刺之前】项目可行性研究报告
    【Sprint3冲刺之前】TDzhushou软件项目测试计划书
    【Sprint3冲刺之前】日历表的事件处理和管理(刘铸辉)
    【Sprint3冲刺之前】项目完成时间表
    【Sprint3冲刺之前】敏捷团队绩效考核(刘铸辉)
    【每日Scrum】第八天(4.29) TD学生助手Sprint2
    【每日Scrum】第七天(4.28)Sprint2总结性会议
    需求分析
  • 原文地址:https://www.cnblogs.com/baihualiaoluan/p/11257350.html
Copyright © 2011-2022 走看看