乘法逆元


三、乘法逆元

一、定義

  若在mod p意義下,對於一個整數a,有a*b≡1(mod p),那么這個整數b即為a的 乘法逆元,同時a也為b的乘法逆元

        一個數有逆元的充分必要條件是gcd(a,p)=1,此時a才有對p的乘法逆元

 

二、逆元是干什么的呢

首先對於除法取模不成立,即(a / b) % p ≠ (a % p / b % p) % p 。

顯然數學家們是不能忍受這種局面的,他們扔出了“逆元”來解決這個問題。

因為取模運算對於乘法來說是成立的,逆元就是把除法取模運算轉化為乘法取模運算

 

                                                                             (a/b)%p=m  -------- 1

                                     假設存在一個數x滿足         a*x%p=m ------- 2

 

由模運算對乘法成立,對1式兩邊同時乘以 b ,得到:
                                                                              a%p=(m(b%p))%p
                                    
如果 a 和 b 均小於模數 p 的話,上式可以改寫為:
                                                                              a =bm%p

等式兩邊再同時乘以 x, 聯立2式比較得到:
                                                                              ax%p=m%p=xbm%p

因此可以得到:
                                                                              bx%p=1

哎,x是b的逆元呀(x 在模運算的乘法中等同於 1/b, 這就是逆元的意義)

 

由以上過程我們看到,求取 (a/b)%p 等同於 求取 a∗(b的逆元)%p。 因此,求模運算的除法問題就轉化為就一個數的逆元問題了。

另一個角度,不僅僅是為了除法取模。比如輸入任意互質的兩個數b,p,要使等式b%p=1恆成立,顯然,要使等式恆成立必須借助一個數x(把這個數叫做b的逆元),使得bx%p==1

eg :b=22,p=5

顯然22%5!=1

22*3%5==1,22的逆元x=3;

 

三、求取一個數的逆元,有兩種方法

 

 

  (1).費馬小定理

    因為在算法競賽中模數p總是質數,所以可以利用費馬小定理 :

    bp1%p=1

              可以直接得到 b 的逆元是 bp2, 使用 快速冪 求解即可

    

    所以bp-2即為b在 mod p 意義下的逆元

 

 

ll pow(ll a, ll n, ll p)    //快速冪 a^n % p
{
    ll ans = 1;
    while(n)
    {
        if(n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}

ll niyuan(ll a, ll p)   //費馬小定理求逆元
{
    return pow(a, p - 2, p);
}

 

 

 

 

 

 

       (2)擴展歐幾里德

              對於利用拓展歐幾里德算法求逆元,很顯然,如果bx%p=1,那么 bx+py=1(p=0,解x),直接利用 exgcd(b, p, x, y),則 (x%p+p)%p 即為 b 的逆元((x%p+p)%p為x的最小正整數解)。

 

void exgcd(ll a, ll b, ll &x, ll &y)    //拓展歐幾里得算法
{
    if(!b) 
        x = 1, y = 0;
    else
    {
        exgcd(b, a % b, y, x);
        y -= x * (a / b);
    }
}

ll niyuan(ll a, ll b)   //求a對b取模的逆元
{
    ll x, y;
    exgcd(a, b, x, y);
    return (x + b) % b;
}

 

 

 

 模板題:https://www.cnblogs.com/-citywall123/p/10693865.html

 

 


來源https://blog.csdn.net/arrowlll/article/details/52629448

 

 

 

 

給定a,n求出 S=((((aa)a)a)a)a (mod 998244353)na。

輸入:

輸入僅一行. 兩個正整數a和n

輸出:

輸出僅一行. 一個正整數


樣例解釋:

 ((22)2)2mod 998244353 = 256

 

數據范圍

a,n <= 1018

 思路:

               先求出指數,即 an-1(快速冪求解),並將指數對mod-1(因為mod是質數,那么φ(mod)= mod-1),再用更新后的指數做為新的指數用快速冪求解即可。

代碼如下:

 

#include<cstdio>
typedef long long ll;
ll a,n;
const int M=998244353;
ll mi(ll a,ll b,int mod)
{
    ll re=1; a%=mod;
    while (b)
    {
        if (b&1) re=(re*a)%mod;
        a=(a*a)%mod;
        b>>=1; 
    }
    return re;
}
int main()
{
    scanf ("%lld%lld",&a,&n);
    ll t=mi(a,n-1,M-1);
    printf("%lld",mi(a,t,M));
    return 0;
} 

 

 

 

 

 

課后例題://poj 3696

L,L <= 2*109. 問多少個8連在一起的數是L的倍數。如果不存在就輸出0.

//這里省略輸入輸出規則,請讀者自行注意

 

思路:

x個8連在一起可以寫成8*(10x-1)*9,假設d=gcd(L,8)。那么題目可以表達為:L | 8*(10x-1)*9 , 接下來我們做一些簡單的式子變形:

L | 8*(10x-1)/9  ←→  L*9 | 8*(10x-1) ←→  9L/d | (10x-1) ←→  10x ≡ 1 (mod 9L/d)

引理對於任意互質的正整數a,n滿足:ax≡1(mod n)最小的整數值 X0 是φ(n)的約數。

證明如下:

  (反證法)假設X0不是φ(n)的約數,則φ(n)可以表示為:qX0 + r(0 <= r < X0)。題設有:aX0≡1(mod n),那么,aqX0≡1(mod n)且正整數a,n互質,所以有歐拉定理:

aφ(n)≡1(mod n),即aqX* ar≡1(mod n),繼而得出:ar≡1(mod n),此時r<X0,這與X0是最小的整數值矛盾,所以假設不成立。得證。

φ(9L/d)並將其約數帶入10x ≡ 1 (mod 9L/d)驗證是否成立即可。求歐拉函數和快速冪,時間復雜度為:O(√(n) * log2 n)。代碼如下:

 

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n,mod;
int Case;
ll gcd(ll a,ll b)
{
    return a%b==0 ? b : gcd(b,a%b);
}
ll Er(ll x)
{
    ll re=x;
    for (ll i=2;i*i<=x;i++)
    {
        if (x%i==0)
        {
            re=re/i*(i-1);
            while (x%i==0) x/=i;
        }
    }
    if (x>1) re=re/x*(x-1);
    return re;
}
ll mul(ll a,ll b,ll p)
{
    ll re=0;
    while (b)
    {
        if (b&1) re=(re+a)%p;
        a=2*a%p;
        b>>=1;
    }
    return re;
}
ll ksm(ll a,ll b,ll p)
{
    ll re=1; a%=p;
    while (b)
    {
        if (b&1) re=mul(re,a,p);
        a=mul(a,a,p); b>>=1;
    }
    return re;
}
int main()
{
    while (scanf ("%lld",&n))
    {
        if (n==0) return 0;
        Case++;
        ll d=gcd(n,8);
        ll phi=Er(9*n/d);
        mod=9*n/d;
        ll flag=9223372036854775806;
        for (ll i=1;i*i<=phi;i++)
        {
            if (phi%i==0)
            {
                if (ksm(10,i,mod)==1) 
                  flag=min(flag,i);
                if (ksm(10,phi/i,mod)==1)
                  flag=min(flag,phi/i);
            }
        }
        flag==9223372036854775806?printf("Case %d: 0\n",Case):printf("Case %d: %lld\n",Case,flag);
    }
} 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM