密码学中模运算的逆元求解

  • 对于正整数a和m,如果有ax≡1(modm),那么把这个同余方程中的x最小正整数解叫做a模x的逆元。
    逆元一般用扩展欧几里得算法来求得,如果m为素数,那么还可以根据费马小定理得到逆元为在这里插入图片描述
    推导过程:
    在这里插入图片描述
    详细见:欧拉定理与费马小定理

要了解欧拉定理,就要先了解欧拉函数:
首先什么是欧拉函数呢?

欧拉函数phi(n)就表示1-n中与n互质的数的个数

设Xi为1-n中与n互质的数(一共有phi(n)个)

那么我们可以知道phi(2) = 1 , phi(3) = 2…,我们也可以通过如下的递推式获得更大的欧拉函数的值

对于任意一个能被n整除的质数,有m = n/p

当m%p == 0 的时候,phi(n) = phi(m)*p

当m%p != 0的时候,phi(n) = phi(m)*(p-1)

下面给出一种证明方法:

-------------------------------------------欧拉函数的推导式的推导和证明-----------------------------------------------------

1)由质因数分解定理得,在n中至少能够分解出一个素数p,导致 n = pm,那么可以将1~n划分为一个区间的集合{ [m(i-1) , mi ] , 1<=i<=p },则对于每个区间[m(i-1)+1,mi],其中的每个数可以看做m(i-1)+j (1<=j<=m)。

2)所以gcd ( m*(i-1)+j , m )

= gcd ( m , (m*(i-1)+j)%m ) (根据秦九韶辗转相除法)

=gcd ( m , j )

那么也就是每个区间对应位置的数均与1m中的某个数gcd相同,互质便是gcd(a,b)==1,所以1n中与m互质的数有p*phi(m)个。

3)那么我们接下来进行分类讨论:

若m%p == 0 , 那么 m = ap(a为一个正整数),所以若gcd(b,m) == 1 ,那么 gcd ( b , p ) == 1 , 所以 gcd ( mp , b ) == 1 , 即gcd ( n , b ) == 1 ,所以在这种情况与m互质的数均与n互质,所以phi(n) = phi(m)*p;

若m%p != 0 , 那么若gcd ( b , m) == 1 , gcd ( b , p ) == p , 那么 gcd ( n , b ) == p,所以1~m与m互质的数为q,若m%p != 0 , 那么与m互质且与p不互质的数是q*p,因此在n当中这样的数有phi(m)个,所以 phi(n) = phi(m)*p - phi(m)


-------------------------------------欧拉定理的内容--------------------------------------------------------------------------

a和n互质,那么a^phi(n) == 1 ( mod n )

------------------------------------欧拉定理的证明---------------------------------------------------------------------------

设xi为n当中与n互质的数,一共有phi(n)个:

1)设mi = a * xi , 则mi与n互质

因为a与n互质,mi也与n互质,所以说这个两个因数当中都没有与n相同的质因子,所以mi也不可能有,所以mi与n必然互质

2)因为mi与n互质,故mi不能被n整除

mi = pn+qr

若余数r与n不互质,那么d = gcd ( r , n )!=1 , mi = d*c(c代表一个正整数),那么gcd ( mi , n ) != 1 , 与mi和n互质矛盾!

故mi%n == ri, ri 均与n互质

3)若mi == mj ( mod n ) (i!=j)

mi-mj==a*(xi-xj)(mod n ) == 0 ( mod n )

xi和xj均是小于n的正整数,故xi==xj时才能成立,则与i!=j矛盾

故mi%n的余数均不相同,因为mi的个数与n以内与n以内与n互质的数的个数相同,所以ri就是xi的一个不同的排列。

所以从1n的mi的乘积与1n的xi的乘积模n下同余,所以约掉相同的部分,aaaa…(phi(n)个)==1(mod n ) , 那么原式得证

---------------------------------费马小定理的证明---------------------------------------------------------------------------------

费马小定理其实就是欧拉定理在n是素数时的特例:

a^phi(n) == 1 ( mod n )

当n是素数时,phi(n) = n-1

那么a^(p-1)== 1 ( mod p )


现在来看一个逆元最常见问题,求如下表达式的值:已知a,b
在这里插入图片描述
这个经典的问题有很多方法,最常见的就是扩展欧几里得,如果m是素数,还可以用费马小定理。
但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求与互素。实际上我们还有一种通用的求逆元方法,适合所有情况。公式如下:
在这里插入图片描述
证明步骤如下:
在这里插入图片描述

关于逆元的题目

在这里插入图片描述
参考代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
 
using namespace std;
typedef long long LL;
const int N = 10005;
const int MOD = 9901;
 
bool prime[N];
int p[N];
int cnt;
 
void isprime()
{
    cnt = 0;
    memset(prime,true,sizeof(prime));
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[cnt++] = i;
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}
 
LL power(LL a,LL b)
{
    LL ans = 1;
    a %= MOD;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % MOD;
            b--;
        }
        b >>= 1;
        a = a * a % MOD;
    }
    return ans;
}
 
LL sum(LL a,LL n)
{
    if(n == 0) return 1;
    LL t = sum(a,(n-1)/2);
    if(n & 1)
    {
        LL cur = power(a,(n+1)/2);
        t = (t + t % MOD * cur % MOD) % MOD;
    }
    else
    {
        LL cur = power(a,(n+1)/2);
        t = (t + t % MOD * cur % MOD) % MOD;
        t = (t + power(a,n)) % MOD;
    }
    return t;
}
 
void Solve(LL A,LL B)
{
    LL ans = 1;
    for(int i=0; p[i]*p[i] <= A; i++)
    {
        if(A % p[i] == 0)
        {
            int num = 0;
            while(A % p[i] == 0)
            {
                num++;
                A /= p[i];
            }
            ans *= sum(p[i],num*B) % MOD;
            ans %= MOD;
        }
    }
    if(A > 1)
    {
        ans *= sum(A,B) % MOD;
        ans %= MOD;
    }
    cout<<ans<<endl;
}
 
int main()
{
    LL A,B;
    isprime();
    while(cin>>A>>B)
        Solve(A,B);
    return 0;
}

在这里插入图片描述
参考代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
 
using namespace std;
typedef long long LL;
const int N = 10005;
const int MOD = 9901;
 
bool prime[N];
int p[N];
int cnt;
 
void isprime()
{
    cnt = 0;
    memset(prime,true,sizeof(prime));
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[cnt++] = i;
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}
 
LL multi(LL a,LL b,LL m)
{
    LL ans = 0;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = (ans + a) % m;
            b--;
        }
        b >>= 1;
        a = (a + a) % m;
    }
    return ans;
}
 
LL quick_mod(LL a,LL b,LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = multi(ans,a,m);
            b--;
        }
        b >>= 1;
        a = multi(a,a,m);
    }
    return ans;
}
 
void Solve(LL A,LL B)
{
    LL ans = 1;
    for(int i=0; p[i]*p[i] <= A; i++)
    {
        if(A % p[i] == 0)
        {
            int num = 0;
            while(A % p[i] == 0)
            {
                num++;
                A /= p[i];
            }
            LL M = (p[i] - 1) * MOD;
            ans *= (quick_mod(p[i],num*B+1,M) + M - 1) / (p[i] - 1);
            ans %= MOD;
        }
    }
    if(A > 1)
    {
        LL M = MOD * (A - 1);
        ans *= (quick_mod(A,B+1,M) + M - 1) / (A - 1);
        ans %= MOD;
    }
    cout<<ans<<endl;
}
 
int main()
{
    LL A,B;
    isprime();
    while(cin>>A>>B)
        Solve(A,B);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <bitset>
 
using namespace std;
typedef long long LL;
const int N = 10000005;
 
bitset<N> prime;
 
void isprime()
{
    prime.set();
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}
 
LL ans1[N],ans2[N];
LL inv[N];
 
int main()
{
    isprime();
    int MOD,m,n,T;
    scanf("%d%d",&T,&MOD);
    ans1[0] = 1;
    for(int i=1; i<N; i++)
        ans1[i] = ans1[i-1] * i % MOD;
    inv[1] = 1;
    for(int i=2;i<N;i++)
    {
        if(i >= MOD) break;
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    }
    ans2[1] = 1;
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            ans2[i] = ans2[i-1] * (i - 1) % MOD;
            ans2[i] = ans2[i] * inv[i % MOD] % MOD;
        }
        else
        {
            ans2[i] = ans2[i-1];
        }
    }
    while(T--)
    {
        scanf("%d%d",&n,&m);
        LL ans = ans1[n] * ans2[m] % MOD;
        printf("%lld\n",ans);
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述

发布了81 篇原创文章 · 获赞 25 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43090158/article/details/98945050