一、快速幂
快速幂是用于解决类似 $a^b$ $mod$ $p$值类型的问题的。使用普通的方法是从$1$循环至$b$,再逐次累乘,逐次取模。但这种方法对于$b$很大的时候却可能会超时。那么,这时候我们就需要使用快速幂了。
快速幂是基于以下式子:
若$b$ $mod$ $2=1$,则$a^b=a^frac{b}{2} imes a^frac{b}{2} imes a$
若$b$ $mod$ $2=0$,则$a^b=a^frac{b}{2} imes a^frac{b}{2}$
这样,我们便通过分治将一个大问题变为两个小问题,再逐项计算,并取模。
另外,再加上两个边界条件:
$a^0=1$,$a^1=a$
例如下面的例子:
$3^7=3^3 imes3^3 imes3=(3^1 imes3^1 imes3) imes(3^1 imes3^1 imes3) imes 3$
其中,所有的式子都可以直接进行计算了,其时间复杂度为$Theta(log_2b)$。
其程序如下:
int _pow(int a,int b){ if(b==0)return 1; if(b==1)return a; int res=_pow(a,b/2); if(b%2)return res*res*a; return res*res; }
二、快速乘
快速乘常与快速幂结合在一起。当模数$p$过大时,乘起来可能会超过$long$ $long$的范围。所以,我们也可以借助快速幂的思想在中间优化一下乘法。
注意到乘法$a imes b$可以转化为$underbrace{a+a+...+a}_{b}$,所以我们也能够通过分治方法将其转化。
例如下面的例子:
$3 imes7=3 imes3+3 imes3+3=(3 imes1+3 imes1+3)+(3 imes1+3 imes1+3)+3$
其中,所有的式子也可以进行计算,复杂度依然为$Theta(log_2b)$。
快速乘的程序与快速幂极为类似,只不过将乘换为加而已。
三、矩阵快速幂
矩阵快速幂可以用于求一个某一个函数值是需要从前若干项函数值线性递推过来的函数(也称一次多阶递推式)某一项值。
即求函数$f(n)=a_1f(n-1)+a_2f(n-2)+...+a_mf(n-m)$的$f(n)$的值。普通方法其时间复杂度为$Theta(n imes m)$。在$n$比较大时会出现超时情况。所以,我们应找一个新的实现方法。
这个实现方法即为矩阵。
那么首先,我们需要了解一些关于矩阵的知识。
矩阵是有定义乘法的。但是,不是所有的两个矩阵都能相乘。
两个矩阵$A$和$B$能够相乘,当且仅当$A$的列数等于$B$的行数。
但是,两个矩阵相乘法则,不是这里所讨论的。我们只需要知道如何能够使用矩阵推出$f(n)$。
举个例子,简单的$Fibonacci$数列,我们可以得到:
$egin{bmatrix} f(n) \ f(n-1) end{bmatrix}=egin{bmatrix} 1&1 \ 1&0 end{bmatrix}cdotegin{bmatrix} f(n-1) \ f(n-2) end{bmatrix}$
一直递推下去,就能得到一个矩阵公式:
$egin{bmatrix} f(n+1) \ f(n) end{bmatrix}=egin{bmatrix} 1&1 \ 1&0 end{bmatrix}^ncdotegin{bmatrix} f(2) \ f(1) end{bmatrix}$
再回到一般情况,我们要从
$egin{bmatrix}f(n)\f(n-1)\ vdots\f(n-m)end{bmatrix}$推出$egin{bmatrix}f(n+1)\f(n)\ vdots\f(n-m+1)end{bmatrix}$
其中,$f(n)$,$f(n-1)...f(n-m+1)$在原矩阵中均有出现,所以,通过构造$0$和$1$我们就可以推出。我们现在只需要算出$f(n)=a_1f(n-1)+a_2f(n-2)+...+a_mf(n-m)$。即构造矩阵的第一行为$egin{bmatrix}a_1&a_2&...&a_mend{bmatrix}$。以下所有行分别只含$0$和$1$,即第$i+1$行在第$i$列的值为$1$,其他所有值全为$0$.
其中,矩阵可以进行快速幂,这样就可以通过快速幂的$Theta(log_2n)$时间复杂度解决一次多阶递推式。