快速幂
对于任意底数的幂结果都可以分解为指数为2的幂的结果相乘。
因为任意一个数可以用二进制来表示,所以我们的指数(假设为b),可以写成
(其中,各子项的系数为0或1,因为二进制)所以我们的
比如3^6 = 3^4*3^2
所以我们可以利用以上的结论,思想分解我们的指数为2的幂的多项式的形式,然后依次从低位考察这个多项式,若二进制某一位为0,则该子项为0,那么以该子项为幂的结果就为1,所以我们跳过该位。我们还发现式子2的每一个项都是前一项的平方,所以我们在考察指数多项式的每一位时,不管该位是不是0,都应该记录该子项的结果,方便遇到二进制为1时直接累乘该值即可,减少相乘次数。具体算法如下:
/**
* 快速幂
* @param num 底数
* @param n 指数
* @return
*/
public static int fastPower(int num, int n){
int result = 1;
int digit = num;
while(n != 0){
if((n & 1) == 1){ //该数位为1, 意思分解为幂相乘时,有该数
result *= digit;
}
n >>= 1;
digit *= digit; //指数每右移一位,指数相当于乘以2,对应到幂结果应执行一次平方
}
return result;
}
复杂度分析
普通求幂需要连乘n次,所以复杂度为O(n)。而采用快速幂,把数的相乘按照指数二进制形式的合并了,且合并规模的为上一次的二倍。比如求2的9次方,正常需要连乘9次,而采用快速幂可以降为6次。9的二进制为4位,故需要4次乘法记录当前中间值(该中间值主要为了减少乘法次数,方便遇到二进制为1使用该值),外加2次的二进制为1时需要乘以中间值。因为问题规模为自底向上倍增上去,故复杂度为O(logn)。
矩阵的快速幂
因为矩阵相乘满足结合率,所以,我们可以扩展到二维数组上,也就是求矩阵的幂。而且矩阵的快速幂原理与数的快速幂是一致的,都是利用指数的二进制表现形式,不同的是我们需额外实现矩阵相乘算法。
/**
* 矩阵快速幂
* @param base
* @param index
* @return
*/
public static int[][] fastPowMatrix(int[][] base, int index) {
int[][] res = new int[base.length][base[0].length];
for(int i = 0; i < base.length; i++) {
res[i][i] = 1;
}
int[][] t = base;
while(index != 0) {
if((index & 1) == 1) {
res = multipleMatix(res, t);
}
t = multipleMatix(t, t);
index >>= 1;
}
return res;
}
/**
* 矩阵相乘
* @param left
* @param right
* @return
*/
public static int[][] multipleMatix(int[][] left, int[][] right) {
int[][] res = new int[left.length][left[0].length];
for(int i = 0; i < left.length; i++) {
for(int j = 0; j < right.length; j++) {
for(int k = 0; k < left[0].length; k++) {
res[i][j] += left[i][k] * right[k][j];
}
}
}
return res;
}