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

快速幂

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

an={an2⋅an2,n为偶数a⋅an2⋅an2,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为奇数

还可以这样考虑,将nnn表示为二进制,每一个进制位决定了是否取这一项的结果,且每一项可以由前一项平方迭代计算
an=a(b0b1b2b3⋯bk)2=ab020+b121+b222+b323+⋯bk2k=a1b0⋅a2b1⋅a4b2⋅a8b3⋯abk2k=ab0⋅(a⋅a)b1⋅(a2⋅a2)b2⋅(a4⋅a4)b3⋯(a2k−1⋅a2k−1)bk \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;
}

变形:如果对要求an%ka^n \% kan%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=01,n=1f(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=[1110]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)]=An\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。其中AnA^nAn就可以使用矩阵快速幂计算。

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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值