快速冪取模算法


1.大數模冪運算的缺陷:

快速冪取模算法的引入是從大數的小數取模的朴素算法的局限性所提出的,在朴素的方法中我們計算一個數比如5^1003%31是非常消耗我們的計算資源的,在整個計算過程中最麻煩的就是我們的5^1003這個過程
缺點1:在我們在之后計算指數的過程中,計算的數字不都拿得增大,非常的占用我們的計算資源(主要是時間,還有空間)
缺點2:我們計算的中間過程數字大的恐怖,我們現有的計算機是沒有辦法記錄這么長的數據的,所以說我們必須要想一個更加高效的方法來解決這個問題

2.快速冪的引入:

我們首先從優化的過程開始一步一步優化我們的模冪算法

1.朴素模冪運算過程:

1 #define ans=1
2 for(int i=1;i<=b;i++)
3 {
4     ans*=a;
5 }
根據我們上面說的,這種算法是非常的無法容忍的,我們在計算的過程中出現的兩個缺點在這里都有體現
在這里我們如果要做優化的話,我肥就是每個過程中都加一次模運算,但是我們首先要記住模運算是非常的消耗內存資源的,在計算的次數非常的大的時候,我們是沒有辦法忍受這種時間耗費的

2.快速冪引入:

在講解快速冪取模算法之前,我們先將幾個必備的知識
1.對於取模運算:
  1 (a*b)%c=(a%c)*(b%c)%c 
這個是成立的:也是我們實現快速冪的基礎
之后我們來看看快速冪的核心本質
我通過離散課上的學習,將快速冪的本質差不多理解了一下,感覺還是很深刻的
 
在這里,我們對指數動了一些手腳,核心思想在於
大數的冪運算拆解成了相對應的乘法運算,利用上面的式子,始終將我們的運算的數據量控制在c的范圍以下,這樣我們可以客服朴素的算法的缺點,我們將計算的數據量壓縮了很大一部分,當指數非常大的時候這個優化是更加顯著的,我們用Python來做一個實驗來看看就知道我們優化的效率有多高了
 
 1 from time import *
 2 def orginal_algorithm(a,b,c):  #a^b%c
 3     ans=1
 4     a=a%c  #預處理,防止出現a比c大的情況
 5     for i in range(b):
 6         ans=(ans*a)%c
 7     return ans
 8 
 9 def quick_algorithm(a,b,c):
10     a=a%c
11     ans=1
12     #這里我們不需要考慮b<0,因為分數沒有取模運算
13     while b!=0:
14         if b&1:
15             ans=(ans*a)%c
16         b>>=1
17         a=(a*a)%c
18     return ans
19 
20 time=clock()
21 a=eval(input("底數:"))
22 b=eval(input("指數:"))
23 c=eval(input("模:"))
24 print("朴素算法結果%d"%(orginal_algorithm(a,b,c)))
25 print("朴素算法耗時:%f"%(clock()-time))
26 time=clock()
27 print("快速冪算法結果%d"%(quick_algorithm(a,b,c)))
28 print("快速冪算法耗時:%f"%(clock()-time))

 

 

 

 

我們現在知道了快速冪取模算法的強大了,我們現在來看核心原理:

對於任何一個整數的模冪運算
a^b%c

對於b我們可以拆成二進制的形式
b=b0+b1*2+b2*2^2+...+bn*2^n
這里我們的b0對應的是b二進制的第一位

那么我們的a^b運算就可以拆解成
a^b0*a^b1*2*1...*a^(bn*2^n)
對於b來說,二進制位不是0就是1,那么對於bx為0的項我們的計算結果是1就不用考慮了,我們真正想要的其實是b的非0二進制位

那么假設除去了b的0的二進制位之后我們得到的式子是
a^(bx*2^x)*...*a(bn*2^n)
這里我們再應用我們一開始提到的公式,那么我們的a^b%c運算就可以轉化為

(a^(bx*2^x)%c)*...*(a^(bn*2^n)%c)
這樣的話,我們就很接近快速冪的本質了

(a^(bx*2^x)%c)*...*(a^(bn*2^n)%c)
我們會發現令
A1=(a^(bx*2^x)%c)
...
An=(a^(bn*2^n)%c)
這樣的話,An始終是A(n-1)的平方倍(當然加進去了取模勻速那),依次遞推

 我們可以得出以下的結論:

1.如果b是偶數,我們可以記
k = a2 mod c,那么求 (k)b/2 mod c就可以了。 
2.如果b是奇數,我們也可以記 ((k)b/2 mod c × a ) mod   c  就可以了。

現在我們來考慮實現它:

迭代法:

 1 int fast_pow(int a,int b,int c)
 2 {
 3     int ans=1;   ///記錄結果
 4     a=a%c;   ///預處理,使得a處於c的數據范圍之下
 5     while(b!=0)
 6     {
 7         if(b&1)///奇數
 8         {
 9             ans=(ans*a)%c;///消除指數為奇數的影響
10         }
11         b>>=1;    ///二進制的移位操作,不斷的遍歷b的二進制位
12         a=(a*a)%c;   ///不斷的加倍
13     }
14     return ans;
15 }

 

遞歸法:

 1 ll fast_pow(ll x,ll n,ll p)
 2 {
 3     ll temp;
 4     x=x%p;
 5     if(n==0)///終止條件
 6     {
 7         return 1;
 8     }
 9     temp=fast_pow((x*x)%p,n>>1,p);
10     if(n&1)
11     {
12         temp =temp*x%p;///消除指數為奇數的影響
13     }
14     return temp%p;
15 }

 

 

在這里還要進行幾點說明:

1.二進制的幾個運算符&  和 >> 。

  &運算通常用於二進制取位操作,例如一個數 & 1 的結果就是取二進制的最末位。還可以判斷奇偶,x&1==0為偶,x&1==1為奇。

   >>運算比較單純,二進制去掉最后一位,移位操作,不斷遍歷b的二進制位。

2. a=(a*a)%c這一步的作用是用來不斷的加倍,在先不看同余定理的情況下,a*a==a^2,下一步再乘,就是a^2*a^2==a^4,然后同理  a^4 * a4 =a^8 .........?是不是做到了

    a-->a^2-->a^4-->a^8-->a^16-->a^32.......指數正是 2^i 啊,再看上面的例子,a¹¹ =  a^(2^0) * a^(2^1) * a^(2^3),這三項是不是完美解決了,快速冪就是這樣。

 


免責聲明!

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



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