一个 n×m 的矩阵是一个由 n 行 m 列元素排成的矩形阵列。矩阵里的元素可以是数字符号或者数学式。
形如(acbd)的数表称为二阶矩阵,它由二行二列组成,其中a,b,c,d称为这个矩阵的元素。
形如(x1x2)的有序对称为列向量(Column Vector)
矩阵的加法非常简单
对,就是你想的那样
a1,1a2,1⋮an,1a1,2a2,1⋮an,2⋯⋯⋯a1,ma2,m⋮an,m±b1,1b2,1⋮bn,1b1,2b2,1⋮bn,2⋯⋯⋯b1,mb2,m⋮bn,m=a1,1±b1,1a2,1±b2,1⋮an,1±bn,1a1,2±b1,2a2,1±b2,2⋮an,1±bn,2⋯⋯⋯a1,m±b1,ma2,m±b2,m⋮an,m±bn,m
注意:两个矩阵必须为同型矩阵(即必须都是 n×m 的矩阵)才能相加
并且,矩阵加减法满足交换律和结合律(即A+B=B+A,(A+B)+C=A+(B+C))
矩阵乘法稍有不同
嗯,真的只有一点不同
如果有矩阵 A 大小为 n×m ,矩阵 B 大小为 m×s ,相乘为矩阵 C,矩阵 C 的大小一定为 n×s
矩阵乘法遵循 C=∑k=1mai,kbk,j
嗯,只有一点
设
AB=(acbd)=(x1x2)
则
C=AB=(ax1+bx2cx1+dx2)
称为二阶矩阵 A 与平面向量 B 的乘积,记为 AB=C
众所周知,斐波那契数列从第三项开始,每一项都是前两项之和
即Fn=Fn−1+Fn−2,n≥3
特别的F0=0,F1=F2=1
把斐波那契数列中相邻的两项( Fn 和 Fn−1 )写成一个2×1的矩阵
=(FnFn−1)=(Fn−1+Fn−2Fn−1)=(1×Fn−1+1×Fn−21×Fn−1+0×Fn−2)=(1110)×(Fn−1Fn−2)=(1110)n−1×(F1F0)=(1110)n−1×(10)
求 Fn 等同于求二阶矩阵的 n−1 次方,结果取矩阵第一行元素。
问题转换为二阶矩阵的 n 次幂
这里可以回顾一下矩阵乘法
假设计算矩阵 A 的 N 次幂
二阶矩阵的乘法满足结合律
设A,B,C都是任意的二阶矩阵
则A(BC)=(AB)C
不在此证明
设n=N÷2 (结果向下取整)
若 N∈2k 则 AN=An×An
若 N∈2k+1 则 AN=An×An×A
这样可以减少计算次数,自行思考原因
以计算 A6 为例
例如6(10)=110(2)
则 A6=A4×A2
二进制位上图显示二进制与幂的指数关系
二进位为 1 需要乘,为 0 不需要乘
再例如7(10)=111(2)
则A7=A4×A2×A1
先随随便便写一个求快速幂的代码(相信大家都会写吧
int qpow(int a, int k) {
int res = 1;
while(k) {
if(a & 1) res *= a;
k >>= 1;
a *= a;
}
return res;
}
那么到底怎么用矩阵求斐波那契数列呢?
#include<cstdio>
#include<cstring>
struct Matrix {
long long fib[2][2];
Matrix() {memset(fib, 0, sizeof fib);}
};
Matrix qmul(Matrix &a, Matrix &b) {
Matrix c;
for(int i = 0; i < 2; ++i) {
for(int j = 0; j < 2; ++j) {
for(int k = 0; k < 2; ++k) {
c.fib[i][j] += a.fib[i][k] * b.fib[k][j];
}
}
}
return c;
}
Matrix qpow(Matrix a, long long k) {
Matrix ans;
ans.fib[0][0] = ans.fib[1][1] = 1;
while(k) {
if(k & 1) ans = qmul(ans, a);
a = qmul(a, a);
k >>= 1;
}
return ans;
}
int main() {
long long n;
Matrix a, b;
scanf("%lld", &n);
a.fib[0][0] = a.fib[0][1] = a.fib[1][0] = 1;
b = qpow(a, n);
printf("%lld\n", b.fib[0][1]);
return 0;
}