轉載自:https://blog.csdn.net/dbc_121/article/details/77646508
快速冪取模的用途:在ACM這類競賽中,可能會遇到指數型的數據取模問題,這個時候如果直接用int或者long long儲存,就
有可能會超出計算機整數的存取范圍,而導致數據出錯。所以我們需要一種方法進行計算。而這種方法就是我們這次要講到
的快速冪取模(簡稱快速冪)。這種算法在時間和空間上都做了盡可能的優化,所以學會之后,會覺得非常好用。
快速冪取模的思路:快速冪實現的最基本的理論就是我們離散課上或者數論中學過的一條公式推出的引理。
引理:積的取余等於取余的積的取余。
再在這條引理的基礎之上,對指數型數據進行拆分以及合並,從而得到我們用的快速冪算法。
本文我就不用例題講解,直接對快速冪進行解析。畢竟快速冪的算法相對簡單,而且適用題型較為一致。
快速冪具體分析:對a^b進行分析。
對於當a和b較小是直接用int或者long存是沒有問題的,但是當a和b大到一定程度時,這就不是暴力存就
可以解決的問題了。我們應該怎么去解決這個問題呢?
在這里我們需要把注意力放在“大”字上面,正是由於a和b過大才導致的問題。所以我們要想辦法不斷地減
小a和b的規模,所謂逐個擊破。
根據上面的那條引理,我們知道了可以把指數拆開,從這個突破口突破。這里我們就不難想到這樣一個算法:
//①a是底數,b是指數,mode是取模數,sum是記錄取模的結果
int sum = 1; a = a % mode; for(int i = 1; i <= b; i++) { sum = sum * a; } sum = sum % c;
這是直接利用的 引理而寫出來的代碼,這只是單純的降低的a的規模,但是這還達不到我們的要求,所以我們
需要進一步改進算法。(當然還可以繼續降低啊的規模,即將循環中的那句換成sum = (sum * a)%mode)
我們已經實現的降低a的規模,所謂我們要想着怎么降低b的規模。我們首先看兩個樣例:先看b為偶數的樣例
7^16,mode = 3,我們要怎么進行拆分?
最基本的拆分是這樣的:7*7*7*7*7*7*7*7*7......7,上面的算法只是將其變為2*2*2*2*2*2*2......2,那么怎么減少b
的規模呢?你應該有一點感覺了吧。就是兩兩合並,將(7^16)變成(49^8),這就降低了b的規模,再利用上面
的算法降低a的規模,最終會變成1 *1*1*1*1*1*1*1。是不是感覺整個數簡單了很多。
按照這個思路,我們可以多循環幾次,從而不斷的降低a和b的規模。
那么b為奇數怎么辦呢?
其實也很簡單,我們只要在偶數算法的基礎之上,每次把多出來的這個數跳出來,預先取模再帶入即可。
從而最終得出快速冪的代碼
long long Mode(long long a, long long b, long long mode) { long long sum = 1; a = a % mode; while (b > 0) { if (b % 2 == 1) //判斷是否是奇數,是奇數的話將多出來的數事先乘如sum
sum = (sum * a) % mode; b /= 2; a = (a * a) % mode;// 不斷的兩兩合並再取模,減小a和b的規模
} return sum; }
當然有時候你可能會碰到用&的運算符的代碼實現,其實和這個大致相同,只不過是用&操作符對b的奇偶性進行判斷而已
&的操作符:二進制位中,1 & 1 = 1,其余組合均為0
附上用&實現的代碼
long long Mode(long long a, long long b, long long mode) { long long sum = 1; while (b) { if (b & 1) { sum = (sum * a) % mode; b--; } b /= 2; a = a * a % mode; } return sum; }
總結:快速冪雖然是個“小”算法,但是有需要用到它的時候非常有用,所以不能小看它,只要稍加功夫去理解,快速冪
還是很好學的,當然一切都需要你在題目中得到鍛煉。
