數論模運算以及快速冪小解

江上舟搖發表於2022-04-14

來到數論王國,一切都得重新開始啦

模運算,顧名思義,對一個數進行取模運算,在大數運算中,模運算是常客

如果一個數太大無法直接輸出,或者是不需要直接輸出,可以對他進行取模縮小數值在輸出

我們習慣這樣寫:a%b=c

取模的結果一般滿足於0<=c<=m-1,m一般是題目給的資料範圍

而對於取模操作,滿足一下的性質:

(a+b)%c=((a%c)+(b%c))%c;

(a-b)%c=((a%c)-(b%c))%c;

(a*b)%c=((a%c)*(b%c))%c;

而除法就不行了,除法如果進行上面的取模操作是錯誤的,

那怎麼辦呢?對於除法取模操作,在同餘和逆元中會展開講解,這裡我李某人先講講所謂的基本數論操作

那取模運算我們就算講完了,接下來我們來講講所謂的快速冪運算

冪運算我們老生常談,而快速冪就為了快速解決冪次運算的,當一個數很大的時候進行冪次運算的時候,一般兩種情況:要麼炸,要麼超時

我們可以採取一下兩種辦法來解決這個問題:

一、

我們可以很容易想到,先算a^2,在a^2^2,一直算到結束,我們可以用遞迴分治的思想來解決

long long fastpow(long long a,long long b)
{
    if(b==0)
    return 1;
    long long temp=(fastpow(a,b/2));//分治遞迴了
    if(b%2==1)//如果是奇數次,還要多乘一個
    return (temp*temp*a);
    else //偶數次正好乘完
    return (temp*temp);
}

這種方法是較為好想到的;

我們再來介紹另外一種方法--其實也是老生常談了,只不過換了一個形式--我們在多重揹包中所見的二進位制優化

將a^11分解成a^8,a^2,a^1的乘積

而a^2=a^1*a^1,a^4=a^2*a^2;

a^8同理,都是二的倍數,產生的a^i都是倍數關係,一步一步遞迴就可以了

另一方面,我們在分解像n這樣的數字的時候,我們也可以採取二進位制的思想,我們很清楚 ,對於二進位制來說,二進位制的前一位數字的值都比低一位的數字的值多2倍;

舉個例子來講;

我們用10進位制來表示11;

那對應的2進位制就是1011;

就可以表示成2^3+2^1+2^0

還有一個需要考慮的是,對於二進位制中的0,我們可以採用二進位制中的位運算的方法來跳過:

(1)n&1,如果不是1,就直接跳了就?了;

(2)n>>=1,和n<<=1的功能是相反的,那個是向左位運算,相當於每次進行二次方,而這次是向右運算,相當於每次縮小2次方

long long fastpow(long long a,long long b)
{
    long long base=a;//底數
    long long res=1;//結果
    while(b)
    {
        if(b&1)//如果這一位是,就要對結果進行累乘了
            res=(base*res);

        base=(base*base);//遞推運算,a^2->a^4->a^8->a^16
        b=b>>=1;//進行位移運算
    }
    return res;
}

而對於快速冪取模

在快速冪進行取模操作,直接對a^n取模,和先對a取模在做冪次操作的效果是相同的;

所以快速冪取模有這樣的性質:a^n%b=(a%b)^n%b;

講了這麼多,拿個題練練手

題目連結:https://www.luogu.com.cn/problem/P1226

題目的兩種解法:

一、遞迴分治法:

 1 #include<bits/stdc++.h>//快速冪分治遞迴法 
 2 using namespace std;
 3 long long  a,b,p; 
 4 long long fastpow(long long a,long long b)
 5 {
 6     if(b==0)
 7     return 1;
 8     long long temp=(fastpow(a,b/2));
 9     if(b%2==1)
10     return (temp%p*temp%p*a%p)%p;
11     else 
12     return (temp%p*temp%p)%p;
13 }
14 int main()
15 {
16     std::ios::sync_with_stdio(false);
17     cin>>a>>b>>p;
18     long long ans=fastpow(a,b);
19     ans=ans%p;
20     printf("%lld^%lld mod %lld=%lld\n",a,b,p,ans);
21     return 0;
22 }

快速冪二進位制優化:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 long long a,b,p;
 4 long long ans;
 5 long long fastpow(long long a,long long b)
 6 {
 7     long long base=a;
 8     long long res=1;
 9     while(b)
10     {
11         if(b&1)
12             res=(base%p*res%p)%p;
13         base=(base%p*base%p)%p;
14         b=b>>=1;
15     }
16     return res;
17 }
18 int main()
19 {
20     std::ios::sync_with_stdio(false);
21     cin>>a>>b>>p;
22     long long ans=fastpow(a,b);
23     ans=ans%p;
24     printf("%lld^%lld mod %lld=%lld\n",a,b,p,ans);
25     return 0;
26     
27 }

 

 

 而對於快速冪來講,還有一個很重要的應用,就是快速冪矩陣運算,這個嘛,賣個關子,明天再講,哈哈哈

 

相關文章