(一) 普通快速幂
- 一般平时我们计算幂次时,都是采用累乘的方法,但这种方法在面临比较大的幂次时,计算效率比较低,时间复杂度为O(n),因此在数据量比较大时,我们要采取更加优化的算法,快速幂就可以很好的解决这个问题.快速幂时间复杂度O( log 2 n \log_2^n log2n)
- 快速幂原理:
以 2 10 2^{10} 210为例,按照累乘计算,则需要计算10次
但是 2 10 2^{10} 210 = 2 8 2^8 28 ∗ {*} ∗ 2 2 2^2 22 = 2 8 + 2 2^{8{+}2} 28+2.
很明显10化为二进制为1010,而8和2都对应该二进制位为1的权重,也就是 2 10 2^{10} 210 = 2 2 3 2^{2^3} 223 ∗ {*} ∗ 2 2 1 2^{2^1} 221,此时只需要计算2次,这个在幂次越大时优化越明显. - 快速幂模板
public static int pow_Num(int a, int b) {
int ans = 1;
while (b != 0) {
if ((b & 1) != 0) {
ans = add_Num(ans, a);
}
a = add_Num(a, a);
b >>= 1;
}
return ans;
}
-
a
b
a^b
ab, a = add_Num(a, a)
a ∗ {*} ∗ a = a 2 a^2 a2
a 2 a^2 a2 ∗ {*} ∗ a 2 a^2 a2 = a 4 a^4 a4
a 4 a^4 a4 ∗ {*} ∗ a 4 a^4 a4 = a 8 a^8 a8
……
以此增加权重 - 除此之外,还可以用快速乘进一步优化,原理和快速幂一样
- 快速乘
public static int add_Num(int a, int b) {
int ans = 0;
while (b != 0) {
if ((b & 1) != 0) {
ans = (ans + a) % MOD;
}
a = (a + a) % MOD;
b >>= 1;
}
return ans;
}
import java.util.Scanner;
public class Demo17 {
static int m;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long a = scanner.nextInt();
long b = scanner.nextInt();
m = scanner.nextInt();
System.out.println(pow_Num(a, b));
}
private static long pow_Num(long a, long b) {
long ans = 1;
while (b != 0) {
if ((b & 1) != 0) {
ans = ans * a;
ans = ans % m;
}
a *= a;
a = a % m;
b >>= 1;
}
return ans;
}
}
(二) 矩阵快速幂
- 矩阵快速幂和普通快速幂的原理一样,难点就在于要正确构建矩阵,在此以斐波那契数列作为例题来说明构建矩阵
- 斐波那契数列:也就是第一项和第二项为1,后面每一项都等于前一项和前两项之和.
也就是 a n a_n an = a n − 1 a_{n{-}1} an−1 + + + a n − 2 a_{n{-}2} an−2
a n a_n an = a n − 1 a_{n{-}1} an−1 + + + a n − 2 a_{n{-}2} an−2
a n − 1 a_{n{-}1} an−1 = a n − 1 a_{n{-}1} an−1
( 1 1 1 0 ) 1 {\begin{pmatrix} 1&1\\ 1&0\end{pmatrix}}^1 (1110)1 ∗ * ∗ ( a n − 1 a n − 2 ) \begin{pmatrix} a_{n{-}1}\\a_{n{-}2}\end{pmatrix} (an−1an−2) = = = ( a n a n − 1 ) \begin{pmatrix} a_n\\ a_{n{-}1}\end{pmatrix} (anan−1)
(
1
1
1
0
)
2
{\begin{pmatrix} 1&1\\ 1&0\end{pmatrix}}^2
(1110)2
∗
*
∗
(
a
n
−
2
a
n
−
3
)
\begin{pmatrix} a_{n{-}2}\\a_{n{-}3}\end{pmatrix}
(an−2an−3)
=
=
=
(
a
n
a
n
−
1
)
\begin{pmatrix} a_n\\ a_{n{-}1}\end{pmatrix}
(anan−1)
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
-----------------------
−−−−−−−−−−−−−−−−−−−−−−−
(
1
1
1
0
)
n
−
2
∗
{\begin{pmatrix} 1&1\\ 1&0\end{pmatrix}}^{n{-}2}*
(1110)n−2∗
(
a
2
a
1
)
\begin{pmatrix} a_2\\a_1\end{pmatrix}
(a2a1)
=
=
=
(
a
n
a
n
−
1
)
\begin{pmatrix} a_n\\ a_{n{-}1}\end{pmatrix}
(anan−1)
- 矩阵快速幂模板
public static int[][] pow_Mat(int[][] mat, int b) {
int[][] ans = new int[n][m];
//单位矩阵
for (int i = 0; i < n; i++) {
ans[i][i] = 1;
}
while (b != 0) {
if ((b & 1) != 0) {
ans = mat_Mul(mat, ans);
}
mat = mat_Mul(mat, mat);
b >>= 1;
}
return ans;
}
//矩阵乘法
public static int[][] mat_Mul(int[][] mat, int[][] ans) {
int[][] res = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
res[i][j] += (mat[i][k] * ans[k][j]) % 1000000007;
}
}
}
return res;
}
import java.util.Scanner;
public class Demo3 {
static long n;
static int a1;
static int a2;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//矩阵行数
n = scanner.nextLong();
//矩阵列数
a1 = scanner.nextInt();
a2 = scanner.nextInt();
//构建矩阵
long[][] mat = new long[][]{{3, 4}, {1, 0}};
//幂次
//int b = scanner.nextInt();
long[][] ints = pow_Mat(mat, n - 2);
long result = ints[0][0] * a2 + ints[0][1] * a1;
System.out.println(result % 1000000007);
}
public static long[][] pow_Mat(long[][] mat, long b) {
long[][] ans = new long[mat.length][mat.length];
//单位矩阵
for (int i = 0; i < mat.length; i++) {
ans[i][i] = 1;
}
while (b != 0) {
if ((b & 1) != 0) {
ans = mat_Mul(mat, ans);
}
mat = mat_Mul(mat, mat);
b >>= 1;
}
return ans;
}
//矩阵乘法
public static long[][] mat_Mul(long[][] mat, long[][] ans) {
long[][] res = new long[mat.length][mat.length];
for (int i = 0; i < mat.length; i++) {
for (int j = 0; j < mat.length; j++) {
for (int k = 0; k < mat.length; k++) {
res[i][j] += mat[i][k] * ans[k][j];
res[i][j] %= 1000000007;
}
}
}
return res;
}
}