取模意義下除法的處理&&乘法逆元 理解與求法


乘法逆元的意義**

取余下,有些除號要變逆元(/b = *b^(-1)),有些除號可以消去 ( a /b *b =a) **

逆元 記作 ..^(-1) 之后直接當冪計算了。**

不確定的性質,嘗試能否力所能及地舉幾個反例

 

(a / b) % p = (a%p / b%p) %p (錯)可見取模下若有除法,結果可能不太對。

 

為了實現取模意義下的除法/計算取模意義下的除法算式;對於一些題目,我們必須在中間過程中進行求余,否則數字太大,電腦存不下,那如果這個算式中出現除法,

取模下除號可看做分數。分數為整數還好,否則除號就得看作除法(取模是整數運算,沒有對小數的相關定義)。為整數時分數與除法的結果都一樣,若無逆元則只能看做分數**

 

參考文獻:

https://baijiahao.baidu.com/s?id=1609032096408414934&wfr=spider&for=pc

https://www.luogu.com.cn/blog/1239004072Angel/post-shuo-xue-sheng-fa-ni-yuan

 

逆元:

  逆元素(簡稱逆元)是指一個可以取消另一給定元素運算的元素,如加法中的加法逆元(相反數)和乘法中的倒數。簡單來說,A的逆元就是能抵消A的影響的元素。

數論中的乘法逆元:

  定義:理解也很好,a^(-1) *a =1,即逆元消除了乘a的影響。

 

  作用/意義:實現模意義下的大部分除法。(除法就是求另一個因數,這樣乘上逆元在消除除數就好了)

    結合除法的定義(https://baike.baidu.com/item/%E9%99%A4%E6%B3%95/6280598?fr=aladdin),模意義下的a/b,即為求a在模意義下除了b的另一個因數(設為c),即求b*c≡a (mod p)的c,這時給a乘上b的逆元就消去了乘b的影響,結果為c,即b*c *b^(-1)≡a *b^(-1) ≡c(mod p)。即,模意義下除以一個數就等價於乘這個數的乘法逆元。還說明,到了剩余系中,四則運算的意義較平時會有些微的差別。

  

取模意義下的除法的實現:(常用方法與保底方法)

  取模意義下的除法的本質是求取模意義下的另一個因數。理解本質后就方便干更多事了。

  但有時我們發現並非所有模意義下除法都要必須轉化為乘法逆元去做,而且有些除法還不能用乘法逆元。比如6/2 (mod p):可以求2的逆元x,在算6*x=y(逆元算法);但是6/2是可整除的,可直接得出6除了2的另一個因數(即為結果)為3(直接算)。可以證明兩種算法的結果在[0,p)內是相同的。到了這時就有了一個想法,嘗試驗證當a能被b整除時,商模p后(設為y)也可作為模意義下a/b的答案:

    首先說結論:直接算和逆元算法這兩種算法做取模下的除法的結果都正確且在p的范圍內唯一。y也是滿足模意義下除法的定義的(顯然y*b≡a (mod p)),設通過逆元的方法得出a/b ≡ c (mod p),再對c模p(一般要求的答案都要在p的范圍內)。此時有式子:b*y ≡ b*c ≡ a (mod p),即b*(y-c) ≡ 0 (mod p)。

      當b,p互質時,逆元c才存在(下文有講逆元的存在性),此時p|(y-c),若y,c都小於p大於0的話(可通過模p、小於0時加p解決,本質不變),可知y=c,計算除法的即兩算法都可用,且結果一樣;

      接上文,當b,p不互質時,逆元c不存在,a/b只能用直接算。

  總之取模下的除法可以直接算(能整除)也可以用逆元算(存在逆元)。既不能整除也不存在逆元的話,還有適用性最廣的算法:擴展歐幾里得。

 

  擴展歐幾里得求 除法:由前文除法的定義,求a/b (mod p),即為求解方程b x ≡ a (mod p)。除法有解的充要條件為 gcd(b,p)|a (也是同余方程、轉化的不定方程有解的條件)。由於復雜度為O log n,不如直接算(O 1)和逆元可快速(比 log n 更快)計算時用逆元算,也就在上兩種處理除法的算法條件都不滿足時才適用。

 

  

乘法逆元的性質:

  1、存在唯一性:對 a 來說,若它有逆元,則在p范圍內一定只有一個逆元。

    簡單證明:

    

 

    a*k≡ 0 (mod p)。a有逆元,則a,p互質,則p|k 。a'和a''都在p范圍內,則k=0,矛盾,故假設不成立。

  

  

  2、完全積性函數:(為了接下來方便,我們把 a 的逆元表示為 inv[a] )兩個數的逆元的積等於這兩個數積的逆元, inv[a]*inv[b]=inv[a*b] 。(逆元的積 能抵消原數的積)

    簡單證明:(a*b)* (a^(-1) * b^(-1))=1,所以(a^(-1) * b^(-1))是積的逆元,同時它又是逆元的積。

  

  3、存在的充要條件:a,p互質

乘法逆元的求法:

  1、拓展歐幾里得求逆元:(解等價的不定方程)

      

 

      再解出這個不定方程的x就好。

    

    不定方程與同余方程等價,也說明了同余方程有解(有逆元)的充要條件與不定方程有解的一致。

    時間復雜度:log p

    代碼:

    

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

long long a, b;

int exgcd(long long a, long long b, long long &x, long long &y)
{
    if (b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main()
{
    scanf("%lld%lld", &a, &b);
    long long x = 0, y = 0;
    exgcd(a, b, x, y);
    printf("%lld", (x % b + b) % b);//這里防止出現負數
    return 0;
}

 

   2、歐拉定理求逆元:(求一個快速冪。指數減幾各有意義,記准指數就好)

      

 

       a^φ(p) ≡ a * a^(φ(p)-1)  ≡ 1 (mod p),a^(φ(p)-1)即為a的逆元。

      O sqrt(n)枚舉因數,再套公式:

 

      

 

      1- (1/pi)處理時變形為  /pi * (pi - 1) ,因為pi|n,不會出現有小數的情況。然后再做快速冪。

 

    時間復雜度 log φ(p) + sqrt(n)(時間復雜度是忽略“-1” 等 的常數)(求歐拉函數的復雜度(sqrt(n))在某些情況可以被優化,可見:歐拉函數和線性篩

    代碼:

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

long long n, p;

long long phi(long long x)//求x的歐拉函數
{
    long long res = x, tmp = x;//初始化答案為x
    for (int i = 2; i * i <= tmp; ++i)
    {
        if (x % i == 0)
        {
            res = (res / i) % p * ((i - 1) % p) % p;//找到x的一個質因子,計算其對答案的貢獻
            while (x % i == 0) x /= i;//統計完答案后,除去該質因子
        }
    }
    if (x > 1) res = (res / x) % p * ((x - 1) % p) % p;//防止漏篩質因子
    return res;
}

long long power(long long a, long long b)//快速冪
{
    long long res = 1;
    while (b)
    {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

int main()
{
    scanf("%lld%lld", &n, &p);
    long long tmp = phi(p) - 1;
    printf("%lld", power(n, tmp));
    return 0;
}

 

  3、費馬小定理求逆元:(歐拉定理的特殊情況,不用求歐拉函數了。記准指數)

    

 

    a^(p-1)≡ a * a^(p-2) ≡ 1 (mod p)  。  a^(p-2) 即為逆元。做一個 快速冪就好。

 

 

    時間復雜度 log p

    由於大多數題目的模數都為質數,所以很泛用。

    核心代碼:

inline int power(int a, int b)//快速冪
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = 1ll * res * a % p;
        a = 1ll * a * a % p;
        b >>= 1;
    }
    return res;
}

int main()
{
    int a, b;
    cin>>a>>b;
        printf("%d", (int)(1ll * a * power(b, p - 2) % p));
    return 0;
}

 

以上代碼都沒有逆元存在性的判定,根據具體題目情況,判定該加就加。

  4、線性求逆:(讓i的逆元出現在公式,按照公式遞推)

    要求p 為質數。

      

 

       (邊界條件)

 

      設當前要求i的逆元。對p進行帶余除法,p=ki+r(r<i,1<i<p),將其轉化為同余式會得到:

      (希望讓i^(-1)單獨出現在一邊,要消系數。p%p=0,也可通過這個式子來探索性質。)

 

       

 

       

 

       

    時間復雜度:O n    這里n實際是值域,數值范圍很大時就不好用了。

    代碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 3e6 + 10; 
long long inv[maxn], n, p;

int main()
{
    scanf("%lld%lld", &n, &p);
    inv[0] = 0, inv[1] = 1;//初始化1的逆元為1
    printf("%lld\n", inv[1]);
    for (int i = 2; i <= n; ++i)
    {
        inv[i] = (p - p / i) * inv[p % i] % p;//上文中的式子
        printf("%lld\n", inv[i]);
    }
    return 0;
}

 

  5、離線線性求逆:(算出整體逆元后到着一步步算出個體)

    線性時間求n個給定較大的整數a1,a2,...,an模p意義下的逆元。一般保證全部有逆元,或說 p是質數且a中無p的倍數。

    由於值域很大,不能用普通的線性求逆。

    嘗試前綴積。由逆元的完全積性,前綴積的逆元等於逆元的前綴積

      設pre[i]為前綴積,即為 a1*a2*...*ai;inv_pre[i]為pre[i]的逆元,即為 a1^(-1) *a2^(-1) *...*ai^(-1) ;inv[i]為ai的逆元

      可以發現幾個關系:inv[i]=inv_pre[i]*pre[i-1]  inv_pre[i-1]=inv_pre[i]*ai。

      這樣,算出所有前綴積后,可以先用某個log算法算一個inv_pre[n],然后逐漸算出inv[n],inv_pre[n-1],inv[n-1],....就得到答案了。

    

    了解算法后應該能看出一個問題。前綴積中一旦有一個a沒有逆元,即不與p互質,算法就無法算逆元了。所以用這種算法的話,p一般都是質數且a中無p的倍數,否則要將沒有逆元的a挑出,但這樣就難用線性算法辦了。

    巧妙利用了逆元對原數的消去作用與完全積性,算出整體后再逐一消去算出個體。

    時間復雜度 O (n +log p)

    核心代碼:

    pre[0] = 1;//初始化
    for (register int i = 1; i <= n; ++i)
    {
        read(a[i]);
        pre[i] = 1ll * pre[i - 1] * a[i] % p;//計算前綴積
    }
    inv_pre[n] = power(pre[n], p - 2, p);//求出前綴積的逆元
    for (register int i = n; i > 0; --i)//遞推求逆元
    {
        inv[i] = 1ll * pre[i - 1] * inv_pre[i] % p;
        inv_pre[i - 1] = 1ll * inv_pre[i] * a[i] % p;
    }

    

 

  

     

 

 

      

 

 

 

 

 

 

 

 

 

 

  

 


免責聲明!

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



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