算法|数学与数论|逆元

数学与数论|逆元

1.费马小定理
2.线性递推
3.扩展欧几里得(exgcd)

心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。
在这里插入图片描述

线性同余方程(模线性方程):
线性即未知数都是一次,是最基本的方程,形如ax ≡ b(mod m),其中a,b为整数,m为正整数,x为未知数。该方程可以变形为:ax +by = m,当gcd(a,b)|m时方程有解。

逆元:
如果一个线性同余方程ax ≡ 1(mod b),则x称为a在模b意义下的逆元,记作a-1。一个数x的逆元通常表示为inv(x)。
逆元存在充分必要条件:a和p(模数)互质:gcd(a,p)=1,此时逆元唯一存在。

费马小定理

定义:
若p为素数,a,p互质gcd(a,p)=1,则a(p-1)≡1(mod p)。也有表示为ap≡a(mod p)
(费马小定理必须保证模数p为素数才能使用)

推导:
x=a-1,因为ax≡1(mod p),且a(p-1)≡1(mod p)
a(p-1)≡ax(mod p)
两边同乘x(a-1),x≡a(p-2)(mod p)
即 a的逆元在模p意义下与a(p-2)同模的,所以我们可以直接用快速幂求 a 的逆元 a-1

实践代码:

const int mod = 1e9+7;//模
int qpow(int a,int b){//快速幂
    int ans=1;
    while(b){
        if(b&1) ans*=a,ans%=mod;
        a*=a,a%=mod;
        b>>=1;
    }
    return ans%mod;
}
int inv(int x,int mod){//逆元
    return qpow(x,mod-2);
}
void solve(){
   int x;cin>>x;
   cout<<inv(x,mod);
}

线性递推

费马小定理exgcd都可以快速地在O(logn)的复杂度内计算出单个值的逆元,但在求解一大段连续值的逆元时,很轻易就会超时,因此我们需要找一种方法线性计算逆元。

递推公式:inv[i]=-p/i*inv[p%i] (p为模数)
证明:
设t=p/i(向下取整),k=p%i -> p= i * t + k -> i * t + k ≡ 0(mod p) -> i * t ≡ - k(mod p)
i-1和k-1分别为i和k对于模p的逆元,上式两边同时乘以i-1k-1i * t * i-1 k-1 ≡ -k * i-1 * k-1(mod p)t * k-1 ≡ -i-1(mod p)
用inv来表达上式,即
inv[i] ≡ -t * inv[k](mod p)*
将t和k代换,得 inv[i] = -p/i * inv[p%i] %p

实践代码:

const int N =1e5+10;
const int p = 1e9+7;
int inv[N];
void solve(){
    inv[0]=inv[1]=1;
    //通常计算的时候都是正数,所以再在递推公式中加一个p
    for(int i=2;i<=1e5;i++) inv[i]=(p-p/i*inv[p%i]%p)%p;
}

扩展欧几里得(exgcd)

裴蜀定理
对任意整数 a , b , 存在一堆整数 x , y 满足 a x + b y = g c d ( a , b ) 对任意整数a,b,存在一堆整数x,y满足ax+by=gcd(a,b) 对任意整数a,b,存在一堆整数xy满足ax+by=gcd(a,b)
扩展欧几里得算法(EXGCD)
常用于求 a x + b y = g c d ( a , b ) 的一组可行解。 ax+by=gcd(a,b)的一组可行解。 ax+by=gcd(a,b)的一组可行解。

在欧几里得算法最后一步,有 b = 0 , x = 1 , y = 0 , b=0,x=1,y=0, b=0x=1y=0使得 a ∗ 1 + 0 ∗ 0 = g c d ( a , 0 ) 。 a * 1+0 * 0 = gcd(a,0)。 a1+00=gcd(a,0)若b>0,则 g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a,b) = gcd(b,a mod b) gcd(a,b)=gcd(b,amodb),
假设存在x,y满足 b ∗ x + ( a m o d b ) ∗ y = g c d ( b , a m o d b ) b * x + (a mod b) * y = gcd(b,a mod b) bx+(amodb)y=gcd(b,amodb)
b x + ( a m o d b ) y = b x + ( a − b ⌊ bx + (a mod b) y = bx + (a - b\lfloor bx+(amodb)y=bx+(aba/b ⌋ ) y = a y + b ( x − ⌊ \rfloor)y = ay + b(x-\lfloor ⌋)y=ay+b(xa/b ⌋ y ) \rfloor y) y)
所以令 x ′ = y , y ′ = x − ⌊ x'=y,y'=x-\lfloor x=y,y=xa/b ⌋ y \rfloor y y,就得到了 a x ′ + b y ′ = g c d ( a , b ) ax'+by'=gcd(a,b) ax+by=gcd(a,b)的解。

注意
exgcd求逆元适用于模数不为质数的情况

实践代码:

int exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x);
    y-=(a/b*x);
    return d;
}
void solve(){
    int a,b,x,y;//a b取任意值
    cin>>a>>b;
    cout<<(exgcd(a,b,x,y)==1?(x+b)%b:-1)<<endl;
}

心有猛虎,细嗅蔷薇。再见了朋友~

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值