逆元的应用

简介: 逆元的应用

1.逆元解释:

逆元定义:

来自一个大佬的通俗解释:

逆元基本应用1

我们知道对于取余运算,有以下性质:

但是唯独除法是不满足的!

现证明除法不满足:

而对于一些题目,我们必须在中间过程中进行求余,否则数字太大,电脑存不下,那如果这个算式中出现除法,我们就需要逆元了

现在就要在回到刚才的问题了,除以一个数等于乘上这个数的倒数,在除法取余的情况下,就是乘上这个数的逆元

这样就把除法,完全转换为乘法了!

逆元的五种求法

1.暴力求解,易理解但是时间复杂度巨大,上代码:

int Inverse_element(int a, int p)
{
    a = a % p;
    int flag = 0;
    int result;
    for (int i = 1; i < 100000; i++)
    {
        flag = 0;
        for (int j = 1; j < 100000; j++)
        {
            if (a * i == 1 + p * j)
            {
                result = i;
                flag = 1;
                break;
            }
        }
        if (flag == 1)
        {
            break;
        }
    }
    return result;
}

2.扩展欧几里得算法

扩展欧几里得算法定义:

模数可以不为质数,满足gcd(a,b)=1即可:

当gcd(a,b)=1时,代入extend_gcd(a,b,x,y),得到的非负的x值,就是上面的a-1

时间复杂度O ( l o g ( n ) ) O(log(n))O(log(n))

ll exgcd(ll a, ll b, ll &x, ll &y)
{
  if (b == 0)
  {
    x = 1, y = 0;
    return a;
  }
  ll d, x1, y1;
  d = exgcd(b, a % b, x1, y1);
  x = y1;
  y = x1 - a / b * y1;
  return d;
}
ll inverse(ll a, ll b, ll &x, ll &y)
{
  if (__gcd(a, b) != 1) return -1;
  else
  {
    exgcd(a, b, x, y);
    return (x % b + b) % b;
  }
}

3.欧拉定理和费马小定理:

欧拉定理在数论中,欧拉定理,(也称费马-欧拉定理)是一个关于同余的性质。欧拉定理表明,若n,a为正整数,且n,a互质,则:

φ(n)为欧拉函数(1 − n 中 与 n 互 质 的 数 的 个 数 。 1-n中与n互质的数的个数。1nn(1与任何数互质))。

费马小定理:(只适用于模数为质数的情况 )

n nn为质数且a不为n的倍数时,φ(n)=n-1,所以式子变成:

两边同时除a得:

所以得:

用快速幂求一下,复杂度O ( l o g n ) O(logn)O(logn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, p;
ll qpow(ll a, ll b, ll mod)
{
  ll ans = 1;
  while (b)
  {
    if (b & 1) ans = (ans * a) % mod;
    a = (a * a) % mod;
    b >>= 1;
  }
  return ans % mod;
}
int main()
{
  scanf("%d%d", &n, &p);
  for (int i = 1; i <= n; i++)
  {
    cout << qpow(i, p - 2, p) << "\n";
  }
  return 0;
}

4.线性递推逆元(神奇小饼干法)

只适用于模数为质数的情况

当p为质数时有:

证明:

并且inverse[0]=0,inverse[1]=1,递归或循环从2开始

写成算法就是一个递归,到1为止,因为1的逆元就是1。

int inv(int t, int p) {
    return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
}

这个方法复杂度是O(n),但并不是说比前两个差,它可以在O(n)的复杂度内算出n个数的逆元,上面的算法加一个记忆性搜索就行了。

int inv(int t, int p) {
    return INV[t] = t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6 + 19;
typedef long long ll;
ll n, p;
ll ans[N];
int main()
{
  scanf("%d%d", &n, &p);
  ans[1] = 1;
  for (int i = 2; i <= n; i++)
  {
    ans[i] = (p - p / i) * ans[p % i] % p;
  }
  for (int i = 1; i <= n; i++)
  {
    cout << ans[i] << "\n";
  }
  return 0;
}

5.阶乘逆元法

只适用于模数为质数的情况

#include<cstdio>
#define ll long long
using namespace std;
ll mul(ll a,ll b,ll mod) //快速幂模板
{
  ll ans=1;
  while(b)
    {
      if(b&1) ans=ans*a%mod;
      a=(a*a)%mod;
      b>>=1;
    }
  return ans%mod;
}
ll n,p;
ll c[5000010]={1};
ll f[5000010];
int main()
{
  scanf("%lld%lld",&n,&p);
  for(int i=1;i<=n;i++)
    c[i]=(c[i-1]*i)%p;
  f[n]=mul(c[n],p-2,p); //获得inv(n!)
  for(int i=n-1;i>=1;i--) //递推阶乘的逆元
    f[i]=(f[i+1]*(i+1))%p;
  for(int j=1;j<=n;j++) //转化并输出
    printf("%lld\n",(f[j]*c[j-1])%p);
}
目录
相关文章
|
7月前
|
Java C++
筛法求质数
筛法求质数
58 0
筛质数、分解质因数和快速幂的应用
筛质数、分解质因数和快速幂的应用
65 0
|
7月前
D - 11(逆元好题)
D - 11(逆元好题)
一个求公约数和公倍数的有趣求法
一个求公约数和公倍数的有趣求法
64 0
|
算法 C语言 C++
【数论】最大公约数、约数的个数与约数之和定理
先来科普下什么是约数:当a能被b整除,我们就说b为a的约数,b的倍数为a
131 0
欧几里得算法,既辗转相除法。用于计算正整数a,b的最大公约数
欧几里得算法,既辗转相除法。用于计算正整数a,b的最大公约数
112 0
求一个数n次方后的末尾数(数论/快速幂)
hdu1061-Rightmost Digit hdu1097-A hard puzzle 这两个oj题目思路几乎一样,都是为了快速求出一个数n次方后的末尾数为都多少?
240 0
求一个数n次方后的末尾数(数论/快速幂)
[Codeforces] 1557 C Moamen and XOR(组合数学)
题意: 用 < 2k的数填充到长度为n的数组中,要使得数组中所有数& >= ^,问方案数 显然,当k == 1的时候,答案为1,只有当所有的数全为1的情况才可以满足题意 对于其他的情况{ 用小于2k的数进行填充,那么说明填充的数字的二进制位数最多可以有k kk位, 从数的个数的角度来说,奇数个1^ 之后的结果是1,偶数个1^ 之后的结果是0,而对于0来讲,不管多少个0,^起来结果都是0 只要有一个0,那么说这一位上&之后就是0,而为了让^为0,那么只能够选择偶数个1 如果某一位上&为1,那么^为1的情况需要讨论n的奇偶性
125 0