快速幂
求ab % m=?
①a2=a*a,a4=a2*a2,可以快速算出a2,a4,a8,a16……
②b可以被二进制表示出来,例如13=1101。a13就可以被拆为a8*a4*a1。
③ab % m ==(a % m)b % m,也就是说可以提前模a减少a的规模。
③的证明过程: a%c=x → a=uc+x b%c=y → b=vc+y
ab%c=(uc+x)*(vc+y) % c
=[(uv)c2+(uy+vx)c+xy] % c
=xy % c
∴ab%c=xy%c==(a%c)(b%c)%c,可以提前取模。
int quickpow(int a,int b,int m)
{
int ans=1;a=a%m;
while(b)
{
if(b&1) ans=(ans*a)%m;//位运算,如果b最后一位是1,则乘上a
b>>=1;//b右移一位,相当于准备在下一轮循环中看倒数第二位是否为1
a=(a*a)%mod;//同时a也进行幂数翻倍
}
return ans;
}
矩阵快速幂
矩阵也可以用快速幂,设2X2的矩阵A
A*A=A2,A2*A2=A4,并可快速求出A8,A16……
A13=A8×A4×A1
例如求斐波拉契数列的第n项
由于斐波拉契数列是f(1)=f(2)=1,f(n)=f(n-1)+f(n-2)。
递推式可以写作矩阵递推式:
(
1
1
1
0
)
×
(
F
n
−
1
F
n
−
2
)
=
(
F
n
F
n
−
1
)
\begin{pmatrix} 1 & 1 \\ 1 & 0 \\ \end{pmatrix}× \begin{pmatrix} F_n-1 \\ F_n-2 \\ \end{pmatrix}= \begin{pmatrix} F_n \\ F_n-1 \\ \end{pmatrix}
(1110)×(Fn−1Fn−2)=(FnFn−1)
(可以将F0=0看做是斐波拉契数列的第0项)
可以推知:
(
1
1
1
0
)
n
−
1
(
F
1
F
0
)
=
(
F
n
F
n
−
1
)
\begin{pmatrix} 1 & 1 \\ 1 & 0 \\ \end{pmatrix}^{n-1} \begin{pmatrix} F_1 \\ F_0 \\ \end{pmatrix}= \begin{pmatrix} F_n \\ F_n-1 \\ \end{pmatrix}
(1110)n−1(F1F0)=(FnFn−1)
那么只要计算
(
1
1
1
0
)
n
−
1
\begin{pmatrix} 1 & 1 \\ 1 & 0 \\ \end{pmatrix}^{n-1}
(1110)n−1
而这可则以利用上面快速幂的方法
关键代码
//a要初始化为上面那个矩阵,n是上面的n-1
int quickpow(mat a,int n)//mat内有2x2数组+矩阵乘法
{
mat ans;//在类中用构造函数默认初始化为单位矩阵
while(n)
{
if(n&1) ans=mul(ans,a);//mul是矩阵乘法函数,要自己写,三重循环
n>>=1;//n右移一位,相当于准备在下一轮循环中看倒数第二位是否为1
a=mul(a,a);//同时a也进行幂数翻倍
}
return ans;
}
得到的最后矩阵ans的ans.a[0][0]项就是斐波拉契的第n项,虽然最后还要
a
n
s
×
(
1
0
)
=
(
F
n
F
n
−
1
)
ans×\begin{pmatrix} 1 \\ 0 \\ \end{pmatrix}= \begin{pmatrix} F_n \\ F_n-1 \\ \end{pmatrix}
ans×(10)=(FnFn−1)但很显然Fn的值就是ans.a[0][0]。