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


快速冪是什么

如果要我們求某個數的冪 \(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)。

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


免責聲明!

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



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