快速冪演算法(二分思想減少連乘次數)

Kevin_Matrix發表於2021-08-24

什麼是快速冪

如果要我們求某個數的冪 \(a^{n}\) ,我們的樸素演算法,也就是最最簡單的做法,自然是先設一個表示最終結果的變數ans,初值為1,然後for迴圈n次,每次都用a去乘ans啦,最後ans被乘完之後就是我們的冪的結果。但是如果我們這個數很大的話,那麼就要進行很多次迴圈,這樣速度是很慢的,所以我們會想要用一種演算法來改進,這就叫做快速冪。


快速冪的思路

我們本來的樸素演算法中,如上文所說,就需要設一個變數ans,初值為1,用它表示最後的得數,在過程中要乘很多次底數嘛,最後整個冪計算函式就返回該變數。那麼在快速冪演算法中,我們同樣需要首先設一個ans,初值為1。

對於任意一個冪運算 \(a^{b}\) ,我們的基本思路其實是二分的思想。如果b是偶數,且b/2=c,那麼我們可以將 \(a^{b}\) 轉化成 \((a^{2})^{c}\) ,也就是指數縮小一半而底數變為原來的兩次方。當b很大的時候,這樣做就減少了很多次迴圈(假設b=10000那麼就減少了5000次迴圈)。

但是如果b很大,然後b / 2=c,c也很大,那這個時候還得接著往下除以二,但如果c變成了奇數(或者另一種情況,本來最開始的b就已經是奇數了)這時候應該怎麼辦呢?

現在要對 \(a^{c}\) 進行處理,並且c是奇數,那麼我們可以在 c 中取一個1出來,也就是說將 \(a^{c}\) 化為 \(a^{c-1} \times a^{1}\) 。這樣的話c-1又可以對半分了,然後整個式子中就由左邊的 \(a^{c-1}\) 和右邊的 \(a^{1}\) 組成,右邊部分已經是1次方了不用再優化,左邊的a的偶數次方可以再進行優化。

既然右邊部分已經是a的1次方了,沒法再優化了,那麼每次我們拆出一個a的1次方時,我們就可以直接把它存進上文提到的這個ans中,即ans *= 這個a的一次方。

那麼這樣二分下去,底數會一直變大,而指數會一直變小,到最後指數肯定會縮小到剩餘的部分變為(a的某次方)^2那麼它再分解就是兩個1次方了,根據我們上面的規則,一旦拆出了指數為1 的乘項,就直接把它乘進ans中。那麼最後當指數變為零的時候,迴圈也就結束了,也就得到了我們所要的結果了。


那麼我們可以來看一下上述思想如何實現,計算 \(base^{power}\)就可以這麼寫:

long long fastpower(long long base, long long power) {
	long long ans = 1;
	while (power > 0){
		if ( power % 2 == 0 ){ //指數是偶數,直接除以二
			power = power / 2; //指數縮小一半
			base = base * base;//底數為原來的兩次方
		}
        else{  //指數是奇數
            power = power – 1; //先拆出一個base的一次方,拆完就變成偶數了
            ans = ans * base ; //base的一次方乘進結果中
            power = power / 2; //指數縮小一半
            base = base * base;//底數為原來的兩次方
        }
    }
	return ans;
}


優化

上述的演算法在某些部分還可以進行優化

  1. 對於power = power – 1; 和power = power / 2; 這兩句話,其實可以合併為一句話:power = power/2; 因為在這個分支中power一定是奇數,那麼在C++中,奇數/2 也就是就是它-1再除以2 。

  2. if 和 else 兩個分支其實都要進行power = power / 2 和base = base * base 兩個操作。

    第二個power為奇數的分支其實就是多了個拆出一次冪的過程,那麼我們可以在一開始就進行判斷是否為奇數,如果是就拆出一次冪,如果不是就不做操作,然後進行兩個分支都要進行的操作。

  3. 最後還有一個優化,在程式中需要判斷的是是否為奇數和將這個數除以2的操作,這兩個操作如果改用位運算的話,會顯著提高計算效率。

    對於任意一個數,如果它是奇數,那麼它的二進位制數最低位一定是1,反之一定是0;那麼我們可以使用按位與操作,將這個數與1按位與,這樣的話如果得數是1則說明數字是奇數,而得數是0的話說明該數是偶數,(1就是0000001,其高位全都是0,按位與操作得到的全都會是0) 然後對於a = a/2的操作,也可以用位運算 ,將一個數的二進位制表示每一位都向右移動一位,即為將其除以2。


最簡快速冪演算法模板

為啥這裡加了個mod呢?因為很多題的答案都是非常大的,可能long long都存不下,那這個時候題目就會要求你將所有的答案都以模p的形式給出來。p也就是下面程式碼中的mod。

typedef long long ll;
ll mod_pow(ll base ,ll pow , ll mod){
	ll res = 1;
	while(pow > 0){
        if( pow & 1 ) res = res * base % mod; //pow是奇數
        base = base * base % mod;             //pow是偶數
        pow >>= 1; 
    }
	return res;
}

另外,印象中有一個題中,pow非常大,這個時候需要用到尤拉定理來進行降冪操作,直接用 pow % φ(n)。

具體細節想不起來了,想起來了再更新。

相關文章