数论基础之快速幂(详细教程)
2020.2.17更新,將模板改為c++版,以及增加了對循環(huán)版快速冪的理解
一、問題引入
求 anmodpa^n \ mod \ pan?mod?p 的結(jié)果
分析
- 思路:看到這樣的題目,我們最容易想到就是直接寫一個(gè)n次的循環(huán),每次循環(huán)乘上一個(gè)a,最后再對結(jié)果mod p就能得到結(jié)果。
- 改錯(cuò):為了防止數(shù)據(jù)溢出,我們利用取模的性質(zhì)(a?b)modp=((amodp)?(bmodp))modp(a * b) \ mod \ p=((a \ mod \ p)*(b \ mod \ p)) \ mod \ p(a?b)?mod?p=((a?mod?p)?(b?mod?p))?mod?p,循環(huán)中每次乘a都mod p一次。
- 缺陷:這種暴力法的時(shí)間復(fù)雜度是 O(b)O(b)O(b),如果b的值比較大的話,就很難在題目規(guī)定的時(shí)間內(nèi)計(jì)算出結(jié)果。
二、快速冪
其實(shí)可以利用分治思想對暴力法進(jìn)行優(yōu)化,我們可以這樣想:
an={an/2?an/2,if?nis?evenan/2?an/2?a,if?nis?odda^n = \begin{cases} a^{n/2}*a^{n/2}, & \text {if $n$ is even} \\ a^{n/2}*a^{n/2}*a, & \text{if $n$ is odd} \end{cases} an={an/2?an/2,an/2?an/2?a,?if?n?is?evenif?n?is?odd?
(注意:even是偶數(shù),odd是奇數(shù),b/2的結(jié)果向下取整)
這樣劃分,我們只要求 an/2a^{n/2}an/2 的值就可以的通過一次乘法運(yùn)算求出 ana^nan 的值。而 an/2a^{n/2}an/2 又可以進(jìn)一步分解為 an/4a^{n/4}an/4、an/8a^{n/8}an/8……直到 a1a^1a1,這就是快速冪,這樣的操作的時(shí)間復(fù)雜度僅為 O(logn)O(logn)O(logn) 。
基于對以上思想的理解,我們可以得到快速冪的遞歸版代碼:
ll quickPow(ll a, ll n, ll p) {if (!n) return 1;if (n & 1) {ll t = quickPow(a, n / 2, p);t = (t * t) % p;return (t * a) % p;} else {ll t = quickPow(a, n / 2, p);return (t * t) % p;} }對于快速冪,其實(shí)還有另一種思想,比如我們要求 ana^nan:
- 首先,把指數(shù)轉(zhuǎn)換為二進(jìn)制表示,初始化存儲結(jié)果的變量 ans=1ans = 1ans=1
- 將其二進(jìn)制表示各個(gè)數(shù)位從右到左,以 000 到 k?1k-1k?1 編號,其中 kkk 為該數(shù)的二進(jìn)制表示共有多少位
- 從右到左依次遍歷各數(shù)位,當(dāng)發(fā)現(xiàn)編號為 iii 的數(shù)位為 111 時(shí),計(jì)算 a2ia^{2^i}a2i 的值,并乘進(jìn)結(jié)果 ansansans 中
- 當(dāng)完成所有數(shù)位的遍歷后,得到的 ansansans 即為答案
有可能看了上面的算法步驟描述有點(diǎn)懵,我們接著看下面這個(gè)例子:
對于這種理解,就有了如下的循環(huán)版的快速冪代碼:
ll quickPow(ll a, ll n, ll p) {ll ans = 1;while (n) { //循環(huán)條件相當(dāng)于n != 0if (n & 1) //當(dāng)二進(jìn)制位為1時(shí)ans = (ans * a) % p; a = (a * a) % p; //持續(xù)累計(jì)a^{2^i}的值n >>= 1; //右移1位,準(zhǔn)備在下個(gè)循環(huán)中檢查下個(gè)位數(shù)是否1}return ans; }三、應(yīng)用
1.快速冪用于求解規(guī)模較大的求冪問題
例如:求 anmodpa^{n} \ mod \ pan?mod?p .
2.結(jié)合矩陣乘法求解遞推式求值問題
例如:Fibonacci數(shù)列的第n項(xiàng)
我們知道:
F(n)=F(n-1)+F(n-2)
F(n-1)=F(n-1)+0*F(n-2)
轉(zhuǎn)化成矩陣運(yùn)算可得
(F(n)F(n?1))=(1110)?(F(n?1)F(n?2))\begin{pmatrix} F(n) \\ F (n-1)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} F(n-1) \\F(n-2)\\ \end{pmatrix} (F(n)F(n?1)?)=(11?10?)?(F(n?1)F(n?2)?)
而右邊的21階矩陣又可以進(jìn)一步分解為
(F(n?1)F(n?2))=(1110)?(1110)?(F(n?2)F(n?3))\begin{pmatrix} F(n-1) \\ F (n-2)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}* \begin{pmatrix} F(n-2) \\F(n-3)\\ \end{pmatrix} (F(n?1)F(n?2)?)=(11?10?)?(11?10?)?(F(n?2)F(n?3)?)
按照這樣一直分解下去直到右邊的21階矩陣F(2),F(1),即
(F(n)F(n?1))=(1110)n?2?(F(2)F(1))\begin{pmatrix} F(n) \\ F (n-1)\\ \end{pmatrix}= \begin{pmatrix} 1&1 \\ 1&0 \\ \end{pmatrix}^{n-2}* \begin{pmatrix} F(2) \\F(1)\\ \end{pmatrix} (F(n)F(n?1)?)=(11?10?)n?2?(F(2)F(1)?)
這時(shí)利用矩陣版的快速冪求解其中的矩陣冪乘,就可以在相對較小的時(shí)間復(fù)雜度下得出Fibonacci數(shù)列的第n項(xiàng)的值。
四、實(shí)戰(zhàn)演練
洛谷 P1226 【模板】快速冪||取余運(yùn)算
這是來自洛谷一道比較簡單的模板題,主要是用于檢測快速冪的模板代碼
題目描述
輸入b,p,k的值,求b^p mod k的值。其中b,p,k*k為長整型數(shù)。
輸入格式:
三個(gè)整數(shù)b,p,k.
輸出格式:
輸出“b^p mod k=s”
s為運(yùn)算結(jié)果
輸入樣例#1:
2 10 9
輸出樣例#1:
2^10 mod 9=7
代碼如下
import java.util.*; public class P1226 {static long a,b,n;static long quickPow(long a,long b, long n) {long s=1;while(b!=0) {if(b%2==1) {s=(s*a)%n;}a=(a*a)%n;b/=2;}return s;} // static long quickPow(long a,long b, long n) { // if(b==0) // return 1; // if(b%2==1) { // long t=quickPow(a, b/2, n); // t=(t*t)%n; // t=(t*a)%n; // return t; // }else { // long t=quickPow(a, b/2, n); // return (t*t)%n; // } // }public static void main(String[] args) {Scanner cin=new Scanner(System.in);a=cin.nextLong();b=cin.nextLong();n=cin.nextLong();System.out.println(a+"^"+b+" mod "+n+"="+quickPow(a, b, n)%n);} }//相比較之下還是循環(huán)版的運(yùn)行效率比較高
如果本文對你有所幫助,可以考慮點(diǎn)個(gè)贊哦~
總結(jié)
以上是生活随笔為你收集整理的数论基础之快速幂(详细教程)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蓝桥杯 历届试题 分糖果(模拟)
- 下一篇: 蓝桥杯 算法提高 递推求值(矩阵快速幂)