逆元的应用

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 n n为质数且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);
}
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值