快速幂算法
设计一个算法计算 x n x^n xn的值。
根据定义最常见也最能瞬间想到的是如下的算法:
// 递归写法
public int pow1(int x, int n) {
if (n == 0) return 1;
if (n == 1) return x;
return x * pow1(x, n - 1);
}
// 循环写法
public int pow2(int x, int n) {
int y = 1;
while (n) {
y *= x;
n--;
}
return y;
}
但上面的算法的时间复杂度是 O ( n ) O(n) O(n)
下面采用快速幂算法来解决这个问题。
在解决它之前先来看一下原理: x n = x n − a x a x^{n}=x^{n-a}x^a xn=xn−axa
所以我们可以对本身要求的 x n x^n xn对半分来求,只求一半的数,然后乘以自己本身就可以达到 x n x^n xn
但是会出现的情况就是,对半分的时候会出现小数的情况,所以一定要分奇数和偶数的情况。
如果n分半了之后是偶数,那就直接对半分,如果是奇数则在对半分之后还要乘以一个x。
所以可以有下面的规律:
f(x, n) = {
f(x, n/2)*f(x, n/2), // 当n为偶数
x*f(x, n/2)*f(x, n/2) // 当n为奇数
}
所以得出快速幂算法1:
public int qPow1(int x, int n) {
if (n == 0) return 1;
if (n == 1) return x;
if (n % 2 == 1) return x*f(x, n/2)*f(x, n/2);
return f(x, n/2)*f(x, n/2);
}
但是上面的算法明显没有任何增进,因为f(x, n/2)
要算两次,那和之前的 O ( n ) O(n) O(n)的算法没什么区别。所以使用一个中间变量去接受一下,就可以提高算法效率。
public int qPow1(int x, int n) {
if (n == 0) return 1;
if (n == 1) return x;
int t = f(x, n/2)
if (n % 2 == 1) return x * t * t;
return t * t;
}
上面的快速幂算法还是比较好理解的,下面的快速幂算法就比较的炫技了我觉得,但是也就那样(原理还是上面的,只是不是对半分而已,而是根据进制数来分)。
下面采用二进制数来分。
假设我们要计算的是 x 10 x^{10} x10,那么10的二进制数是1010,所以有如下公式及变换: x 10 = x ( 10 ) 10 = x ( 1010 ) 2 = x 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 0 ∗ 2 0 = x 8 + 2 = x 8 x 2 x^{10}=x^{(10)_{10}}=x^{(1010)_2}=x^{1*2^3+0*2^2+1*2^1+0*2^0}=x^{8+2}=x^8x^2 x10=x(10)10=x(1010)2=x1∗23+0∗22+1∗21+0∗2