快速冪取模運算
阿新 • • 發佈:2019-02-14
1.大數模冪運算的缺陷:
快速冪取模演算法的引入是從大數的小數取模的樸素演算法的侷限性所提出的,在樸素的方法中我們計算一個數比如5^1003%31是非常消耗我們的計算資源的,在整個計算過程中最麻煩的就是我們的5^1003這個過程缺點1:在我們在之後計算指數的過程中,計算的數字不都拿得增大,非常的佔用我們的計算資源(主要是時間,還有空間)缺點2:我們計算的中間過程數字大的恐怖,我們現有的計算機是沒有辦法記錄這麼長的資料的,所以說我們必須要想一個更加高效的方法來解決這個問題2.快速冪的引入:
我們首先從優化的過程開始一步一步優化我們的模冪演算法1.樸素模冪運算過程:
- #define ans=1
- for(int i=1;i<=b;i++)
- {
- ans*=a;
- }
2.快速冪引入:
在講解快速冪取模演算法之前,我們先將幾個必備的知識1.對於取模運算:- (a*b)%c=(a%c)*(b%c)%c
- from
- def orginal_algorithm(a,b,c): #a^b%c
- ans=1
- a=a%c #預處理,防止出現a比c大的情況
- for i in range(b):
- ans=(ans*a)%c
- return ans
- def quick_algorithm(a,b,c):
- a=a%c
- ans=1
- #這裡我們不需要考慮b<0,因為分數沒有取模運算
- while b!=0:
- if b&1:
- ans=(ans*a)%c
- b>>=1
- a=(a*a)%c
- return ans
- time=clock()
- a=eval(input("底數:"))
- b=eval(input("指數:"))
- c=eval(input("模:"))
- print("樸素演算法結果%d"%(orginal_algorithm(a,b,c)))
- print("樸素演算法耗時:%f"%(clock()-time))
- time=clock()
- print("快速冪演算法結果%d"%(quick_algorithm(a,b,c)))
- print("快速冪演算法耗時:%f"%(clock()-time))
- 底數:5
- 指數:1003
- 模:12
- 樸素演算法結果5
- 樸素演算法耗時:3.289952
- 快速冪演算法結果5
- 快速冪演算法耗時:0.006706
- 對於任何一個整數的模冪運算
- a^b%c
- 對於b我們可以拆成二進位制的形式
- b=b0+b1*2+b2*2^2+...+bn*2^n
- 這裡我們的b0對應的是b二進位制的第一位
- 那麼我們的a^b運算就可以拆解成
- a^b0*a^b1*2*...*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)的平方倍(當然加進去了取模勻速那),依次遞推
- int quick(int a,int b,int c)
- {
- int ans=1; //記錄結果
- a=a%c; //預處理,使得a處於c的資料範圍之下
- while(b!=0)
- {
- if(b&1) ans=(ans*a)%c; //如果b的二進位制位不是0,那麼我們的結果是要參與運算的
- b>>=1; //二進位制的移位操作,相當於每次除以2,用二進位制看,就是我們不斷的遍歷b的二進位制位
- a=(a*a)%c; //不斷的加倍
- }
- return ans;
- }
現在,我們的快速冪已經講完了我們來大致的推演一下快速冪取模演算法的時間複雜度首先,我們會觀察到,我們每次都是將b的規模縮小了2倍那麼很顯然,原本的樸素的時間複雜度是O(n)快速冪的時間複雜度就是O(logn)無限接近常熟的時間複雜度無疑逼樸素的時間複雜度優秀很多,在資料量越大的時候,者中優化效果越明顯
3.OJ例題
POJ1995題意:快速冪版題- #include"iostream"
- #include"cstdio"
- #include"cstring"
- #include"cstdlib"
- usingnamespace std;
- int ans=0;
- int a,b;
- int c;
- int quick(int a,int b,int c)
- {
- int ans=1;
- a=a%c;
- while(b!=0)
- {
- if(b&1) ans=(ans*a)%c;
- b>>=1;
- a=(a*a)%c;
- }
- return ans;
- }
- int main()
- {
- int for_;
- int t;
- scanf("%d",&t);
- while(t--)
- {
- ans=0;
- scanf("%d%d",&c,&for_);
- for(int i=1;i<=for_;i++)
- {
- scanf("%d%d",&a,&b);
- ans=(ans+quick(a,b,c))%c;
- }
- printf("%d\n",ans);
- }
- return 0;
- }