矩阵快速幂求斐波那契数列

快速幂

求数 a a a n n n次幂,可以采用二分法进行快速计算,即

a n = { a n 2 ⋅ a n 2 , n 为偶数 a ⋅ a n 2 ⋅ a n 2 , n 为奇数 a^n=\left\{\begin{array}{ll} a^{\frac{n}{2}}\cdot a^{\frac{n}{2}}, & n为偶数\\ a\cdot a^{\frac{n}{2}}\cdot a^{\frac{n}{2}}, & n为奇数 \end{array}\right. an={a2na2n,aa2na2n,n为偶数n为奇数

还可以这样考虑,将 n n n表示为二进制,每一个进制位决定了是否取这一项的结果,且每一项可以由前一项平方迭代计算
a n = a ( b 0 b 1 b 2 b 3 ⋯ b k ) 2 = a b 0 2 0 + b 1 2 1 + b 2 2 2 + b 3 2 3 + ⋯ b k 2 k = a 1 b 0 ⋅ a 2 b 1 ⋅ a 4 b 2 ⋅ a 8 b 3 ⋯ a b k 2 k = a b 0 ⋅ ( a ⋅ a ) b 1 ⋅ ( a 2 ⋅ a 2 ) b 2 ⋅ ( a 4 ⋅ a 4 ) b 3 ⋯ ( a 2 k − 1 ⋅ a 2 k − 1 ) b k \begin{aligned}a^n&=a^{(b_0b_1b_2b_3\cdots b_k)_2}\\ &=a^{b_02^0+b_12^1+b_22^2+b_32^3+\cdots b_k2^k}\\ &=a^{1b_0}\cdot a^{2b_1}\cdot a^{4b_2}\cdot a^{8b_3}\cdots a^{b_k2^k}\\ &=a^{b_0}\cdot (a\cdot a)^{b_1}\cdot (a^2\cdot a^2)^{b_2}\cdot (a^4\cdot a^4)^{b_3}\cdots (a^{2^{k-1}}\cdot a^{2^{k-1}})^{b_k}\end{aligned} an=a(b0b1b2b3bk)2=ab020+b121+b222+b323+bk2k=a1b0a2b1a4b2a8b3abk2k=ab0(aa)b1(a2a2)b2(a4a4)b3(a2k1a2k1)bk

public int power(int a, int n){
	int result = 1;
	while (n > 0) {
		if (n % 2 == 1) { // 还可以用 n & 1 == 1,表示取二进制最后一位
			result *= a; // 二进制最后一位为1,表示取当前a
		}
		n /= 2; // 还可以用 n = n >> 1,表示二进制右移一位
		a *= a; // 迭代计算下一位的基数
	}
	return result;
}

变形:如果对要求 a n % k a^n \% k an%k,对k取余操作可以直接放进每步迭代里

public int power(int a, int n, int k){
	int result = 1;
	while (n > 0) {
		if (n % 2 == 1) {
			result = (result * a) % k; // 对k取余
		}
		n /= 2;
		a = (a * a) % k; // 对k取余
	}
	return result;
}

矩阵快速幂

和上述思路完全一样,只是全部对于矩阵乘法。斐波那契数列定义如下

f ( n ) = { 0 , n = 0 1 , n = 1 f ( n − 2 ) + f ( n − 1 ) , n ≥ 2 f(n)=\left\{\begin{array}{ll} 0, & n=0\\ 1, & n=1\\ f(n-2)+f(n-1), & n\ge2 \end{array}\right. f(n)= 0,1,f(n2)+f(n1),n=0n=1n2

可以构造矩阵 A = [ 1 1 1 0 ] A=\left[\begin{array}{cc}1 \quad 1\\ 1 \quad 0\end{array}\right] A=[1110],则 [ f ( n + 1 ) f ( n ) f ( n ) f ( n − 1 ) ] = A n \left[\begin{array}{cc}f(n+1) \quad f(n)\\ f(n) \quad f(n-1)\end{array}\right]=A^n [f(n+1)f(n)f(n)f(n1)]=An。其中 A n A^n An就可以使用矩阵快速幂计算。

public class Fibonacci {
    static int[][] dot(int[][] A, int[][] B) {
        int Arows = A.length;
        int Acols = A[0].length;
        int Brows = B.length;
        int Bcols = B[0].length;
        assert (Acols == Brows);
        int tmp;
        int[][] R = new int[Arows][Bcols];
        for (int i = 0; i < Arows; i++) {
            for (int j = 0; j < Bcols; j++) {
                tmp = 0;
                for (int k = 0; k < Acols; k++) {
                    tmp += A[i][k] * B[k][j];
                }
                R[i][j] = tmp;
            }
        }
        return R;
    }

    static int fibonacci(int n) {
        if (n == 0) return 0;
        n -= 1;
        int[][] result = new int[][]{{1, 0}, {0, 1}};
        int[][] A = new int[][]{{1, 1}, {1, 0}};
        while (n > 0) {
            if (n % 2 == 1) {
                result = dot(result, A);
            }
            n /= 2;
            A = dot(A, A);
        }
        return result[0][0];
    }

    public static void main(String[] args) {
        System.out.println(fibonacci(100000));
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值