逆元的四种求法

Powered by:NEFU AB-IN

引入:
逆元:每个数 a a a均有唯一的与之对应的乘法逆元 x x x,使得 a x ≡ 1 ( m o d n ) ax≡1(mod n) ax1(modn)

a x ax ax 除以 n n n 的余数为 1 1 1 等价于 a x ≡ 1 ( m o d n ) ax≡1(mod n) ax1(modn)

费马小定理求逆元

费马小定理是数论中的一个重要定理。如果 n n n是一个质数,而整数 a a a不是 n n n的倍数,则有 a n − 1 ≡ 1 ( m o d n ) a^{n-1}≡1(mod n) an11(modn)
变形得: a × a n − 2 ≡ 1 ( m o d n ) a × a^{n-2}≡1(mod n) a×an21(modn)
那么 a a a的逆元便为 a n − 2 a^{n-2} an2

优点:在题目中要求结果对某一质数取模时,此法用逆元很方便。
局限性: a a a n n n互素,且 n n n为质数。

ll inv_fm(ll a, ll n) { return qm(a, n - 2, n); }

扩展欧几里得算法求逆元

贝祖定理:如果 a , b a,b a,b是整数,那么一定存在整数 x , y x,y x,y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
引理1:如果 a x + b y = m ax+by=m ax+by=m有解,那么 m m m一定是 g c d ( a , b ) gcd(a,b) gcd(a,b)的若干倍。
引理2:如果 a x + b y = 1 ax+by=1 ax+by=1有解,那么 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1

e x g c d exgcd exgcd是在求两个数的 g c d gcd gcd上的拓展,既在求出 g c d ( a , b ) gcd(a,b) gcd(a,b)的同时,求出 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)这个方程的一组特解 x , y x,y x,y

  • g c d gcd gcd

    int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);}
    

终止条件为 a = g c d ( a , b ) , b = 0 a = gcd(a,b) ,b = 0 a=gcd(a,b),b=0
带入上述方程中,我们可以得到一组特解 x = 1 , y = 0 x = 1,y = 0 x=1,y=0

  • 注意,此时的 a = g c d ( a , b ) , b = 0 a = gcd(a,b) ,b = 0 a=gcd(a,b),b=0对应此时的特解 x = 1 , y = 0 x = 1,y = 0 x=1,y=0,那如果 要求出最初的 a , b a,b a,b的特解 x , y x,y x,y,就要回到最初的状态,即从这个最终状态利用递归回溯到最初状态。

从最初状态开始: a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
下一个状态便为: b x 1 + ( a % b ) y 1 = g c d ( a , b ) bx_1+(a\%b)y_1=gcd(a,b) bx1+(a%b)y1=gcd(a,b)

如果能推出 x , y x,y x,y x 1 , y 1 x_1,y_1 x1,y1有什么联系,那么往下的递归便好求了。

  • 前置知识: a % b = a − ( a / / b ) × b a\%b=a-(a//b)×b a%b=a(a//b)×b

带入:
b × x 1 + ( a − ( a / / b ) × b ) × y 1 = b × x 1 + a × y 1 – ( a / / b ) × b × y 1 = a × y 1 + b × ( x 1 – a / / b × y 1 ) = g c d ( a , b ) \begin{aligned} b×x_1 + (a-(a//b)×b)×y_1 &= b×x_1 + a×y_1 – (a//b)×b×y_1 \\ &= a×y_1 + b×(x_1 – a//b×y_1) \\ &= gcd(a,b) \end{aligned} b×x1+(a(a//b)×b)×y1=b×x1+a×y1(a//b)×b×y1=a×y1+b×(x1a//b×y1)=gcd(a,b)
发现: { x = y 1 y = x 1 – a / b × y 1 \begin{cases} x = y_1 \\ y = x_1 – a/b×y_1 \end{cases} {x=y1y=x1a/b×y1

  • e x g c d exgcd exgcd

    int exgcd(int a, int b, int &x, int &y){ //x, y 是主函数的值,一开始可以随便赋值,加&是为了x,y的值可以跟随者函数改变而改变
        if(!b){
            x = 1;
            y = 0;
            return a; //此时 a = gcd(a_原始,b_原始)
        }
        int r = exgcd(b, a % b, x, y); //递归到最深层,求出gcd
        int tmp = y; // 步步回退更新x,y
        y = x - (a / b) * y;
        x = tmp;
        return r;
    }
    

回到正题,如何求逆元?

我们已知 a x ≡ 1 ( m o d n ) ax≡1(mod n) ax1(modn) x x x a a a的逆元。这个式子等价于 a x − n k = 1 ax - nk = 1 axnk=1
结合 e x g c d exgcd exgcd的式子
a x + b y = g c d ( a , b ) ax + by = gcd(a,b) ax+by=gcd(a,b)
我们所求的逆元 x x x,便是 e x g c d exgcd exgcd要求的 x x x

ll inv_exgcd(ll a, ll n){
    ll d, x, y;
    d = exgcd(a, n, x, y);
    if(d == 1) //保证gcd=1
        return (x % n + n) % n;
    else
        return -1;
}

局限性: a a a n n n需互素

欧拉定理求逆元

欧拉定理:若 a , n a,n a,n为正整数,且 a , n a,n a,n互质,则 a φ ( n ) ≡ 1 ( m o d n ) a^{φ(n)}≡1(mod n) aφ(n)1(modn)
oula
可以看出,费马小定理其实就是欧拉定理的一种情况:即 n n n为素数时, φ ( n ) = n − 1 φ(n) = n - 1 φ(n)=n1,便有了 a n − 1 ≡ 1 ( m o d n ) a^{n - 1}≡1(mod n) an11(modn)

那么 a a a的逆元为 a φ ( n ) − 1 a^{φ(n)- 1} aφ(n)1

ll euler(ll n){
    ll ans = n;
    for(int i = 2; i * i <= n; i ++){
        if(!(n % i)){
            ans = ans / i * (i - 1);
            while(!(n % i)) n /= i;
        }
    }
    if(n > 1) ans = ans / n * (n - 1);
    return ans;
}
ll inv_euler(ll a, ll n) {return qm(a, euler(n) - 1, n);}

局限性: a a a n n n需互素

线性求逆元

当用逆元的次数比较多时,一直 l o g log log求可能会 T T T,这时线性求出来逆元是个不错的选择

结论

void init()
{
    inv[1] = 1;
    for (int i = 2; i <= N; i ++)
        inv[i] = mul(dec(mod, mod / i), inv[mod % i]); 
}

[蓝桥杯2019初赛]RSA解密

题意:

n=p*q
p,q为素数
d与(p-1*(q-1)互素
(d*e)%((p-1*(q-1))=1
C为密文,x为原文
C=x^d%n
x=C^e%n

给定d,C,n
求x

首先,令 k = ( p − 1 ) ∗ ( q − 1 ) k = (p-1)*(q-1) k=(p1)(q1),那么 d d d k k k互素。
( d × e ) % k = 1   等 价 于   d × e = 1 ( m o d k ) (d×e) \%k=1 \ 等价于 \ d×e = 1(modk) (d×e)%k=1  d×e=1(modk)
我们目标是利用 x = C e % n x=C^e\%n x=Ce%n x x x,那么我们就要求 e e e,那么就是求 d d d关于 k k k的逆元,而 d d d k k k互素,满足欧拉定理和拓展欧几里得,所以可求。

注意

  • 快速幂的结果还是会爆 l o n g   l o n g long \ long long long,所以要加上龟速乘。
/*
 * @Description: 
 * @Author: NEFU AB_IN
 * @version: 1.0
 * @Date: 2021-02-16 17:07:15
 * @LastEditors: NEFU AB_IN
 * @LastEditTime: 2021-03-07 18:17:37
 */
#include<bits/stdc++.h>
using namespace std;
#define ll                          long long
#define ull                         unsigned long long
#define ld                          long double
#define db                          double
#define all(x)                      (x).begin(),(x).end()
#define F                           first
#define S                           second
#define MP                          make_pair
#define PB                          emplace_back
#define SZ(X)                       ((int)(X).size())   
#define mset(s, _)                  memset(s, _, sizeof(s))
#define IOS                         ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl                        "\n"
#define forn(i, l, r)                for (int i = l; i <= r; i++)
typedef pair<int, int>               pii;
typedef pair<ll, ll>                 pll;
const int INF = 0x3f3f3f3f;

ll get_zhishu(ll x){
	for(int i = 2; i * i <= x; i++){
		if(x % i == 0) return i;
	}
}

namespace q_pow_mm{
    ll mm(ll a, ll b, ll m){
        ll res = 0;
        while(b){
            if(b & 1) 
                res = (res + a) % m;
            a = (a * 2) % m;
            b >>= 1;
        }
        return res;
    }
    ll q (ll a, ll b){
        ll ret = 1;
        while(b){
            if(b & 1)
                ret = ret * a;
            a = a * a;
            b = b >> 1;
        }
        return ret;
    }
    ll qm (ll a, ll b, ll c){
        a = a % c;
        ll ret = 1 % c;
        while(b){
            if(b & 1)
                ret = mm(ret, a, c) % c;
            a = mm(a, a, c) % c;
            b = b >> 1;
        }
        return ret;
    }
}
using namespace q_pow_mm;

ll euler(ll n){
    ll ans = n;
    for(int i = 2; i * i <= n; i ++){
        if(!(n % i)){
            ans = ans / i * (i - 1);
            while(!(n % i)) n /= i;
        }
    }
    if(n > 1) ans = ans / n * (n - 1);
    return ans;
}
ll inv_euler(ll a, ll n) {return qm(a, euler(n) - 1, n);}

ll exgcd(ll a, ll b, ll &x, ll &y){
    if(!b){
        x = 1;
        y = 0;
        return a;
    }
    ll r = exgcd(b, a % b, x, y);
    ll tmp = y;
    y = x - (a / b) * y;
    x = tmp;
    return r;
}
ll inv_exgcd(ll a, ll n){
    ll d, x, y;
    d = exgcd(a, n, x, y);
    if(d == 1)
        return (x % n + n) % n;
    else
        return -1;
}

void solve(){
    ll n = 1001733993063167141;
	ll d = 212353;
	ll C = 20190324;
    ll p = get_zhishu(n);
	ll q = n / p;
    ll k = (p - 1) * (q - 1);
    ll e = inv_euler(d, k);
    cout << qm(C, e, n) << endl;

    e = inv_exgcd(d, k);
    cout << qm(C, e, n) << endl;
}

int main()
{
    IOS;
    solve();
    return 0;
}

完结。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NEFU AB-IN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值