摘要
本文主要讲解矩阵乘法和矩阵快速幂。内容不难,都是定理,重点是矩阵乘法的应用。
蓝桥杯知识点汇总:
https://blog.csdn.net/GD_ONE/article/details/104061907
矩阵
数学上,一个的矩阵是一个由行(row)n列(column)元素排列成的矩形阵列。矩阵里的元素可以是数字、符号或数学式。以下是一个由6个数字元素构成的2行3列的矩阵:
-------引用自维基百科。
知道了矩阵是什么后,看看什么是矩阵乘法。
矩阵乘法
数学中,矩阵乘法(英语:matrix multiplication)是一种根据两个矩阵得到第三个矩阵的二元运算,第三个矩阵即前两者的乘积,称为矩阵积(英语:matrix product)。设是的矩阵,是的矩阵,则它们的矩阵积是的矩阵。中每一行的个元素都与中对应列的个元素对应相乘,这些乘积的和就是中的一个元素。
-------引用自维基百科。
直接看图:
矩阵乘法的代码实现就是直接模拟乘法过程,所以没什么好说的,直接给出代码:
public static void mut_mul(long[][] a, long[][] b, long[][] c){ // c = a * b
long[][] t = {{0, 0}, {0, 0}}; // 中间数组
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
for(int k = 0; k < 2; k++)
t[i][j] = (t[i][j] + (a[i][k]*b[k][j]) % mod)%mod;
for(int i = 0; i < 2; i++) c[i] = Arrays.copyOf(t[i], 2);
}
矩阵快速幂
矩阵快速幂和普通快速幂思路完全一样,实现代码的时候将整数乘法变为矩阵乘法就可以了。
代码:
public static void mut_mul(long[][] a, long[][] b, long[][] c){
long[][] t = {{0, 0}, {0, 0}};
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
for(int k = 0; k < 2; k++)
t[i][j] = (t[i][j] + (a[i][k]*b[k][j]) % mod)%mod;
for(int i = 0; i < 2; i++) c[i] = Arrays.copyOf(t[i], 2);
}
public static void qmi(long[][] a, long b, long[][] c){// a = a * c^b
while(b != 0){
if((b & 1) == 1){
mut_mul(a, c, a);
}
b >>= 1;
mut_mul(c, c, c);
}
}
以上就是矩阵快速幂的代码了。
矩阵乘法的应用
矩阵是数学家求解线性方程组的过程中发明的,对于:
将未知数的系数归为一个矩阵,将未知数归为一个矩阵,将结果归为一个矩阵得到:
用第一个矩阵乘以第二个矩阵,就可以得到原来的线性方程组。
所以矩阵相乘更像是一种函数,让第一个矩阵通过某种映射关系,转化到另一个矩阵。
对于斐波那契数列数列:
转化为关系式:
从第三项开始,每一项都是前两项的和,我们能否用矩阵相乘来表示这个关系呢?
设矩阵A为:
设矩阵C为:
矩阵A怎么变为矩阵C呢?
矩阵C的第一个元素为
所以我们构造出一个关系矩阵:
于是可以将 转化为:
对于斐波那契数列来说,关系矩阵是不变的,所以如果要用上式来求斐波那契数列的第的话,我们可以这样做:
即用矩阵乘以关系矩阵n-1次。
这样做的时间复杂度反而比用原本的关系式递推求更高了,但是我们可以对其进行优化,那就是用矩阵快速幂来算关系矩阵的(n-1)次方。这样时间复杂度就降到了级别。
一般要用矩阵乘法求解的问题都要构造一个关系矩阵,能否正确的构造出关系矩阵,就是解题的关键了。
例题:
问题描述
在一个奇怪的星球上驻扎着两个虫群A和B,它们用奇怪的方式繁殖着,在t+1时刻A虫群的数量等于t时刻A虫群和B虫群数量之和,t+1时刻B虫群的数量等于t时刻A虫群的数量。由于星际空间的时间维度很广阔,所以t可能很大。OverMind 想知道在t时刻A虫群的数量对 p = 1,000,000,007.取余数的结果。当t=1时 A种群和B种群的数量均为1。
输入格式
测试数据包含一个整数t,代表繁殖的时间。
输出格式
输出一行,包含一个整数,表示对p取余数的结果
样例输入
10
样例输出
89
样例输入
65536
样例输出
462302286
数据规模和约定
对于50%的数据 t<=10^9
对于70%的数据 t<=10^15
对于100%的数据 t<=10^18
题解:
正解就是构造一个关系矩阵,然后用求斐波那契数列第n项的方式求解答案就可以了,先不看下面的题解,自己尝试构造一下吧。
题目说:在。
我们设矩阵A为, 矩阵C为
则关系矩阵为:
得到关系矩阵后就直接套用矩阵乘法和矩阵快速幂模板就可以了,
AC代码:
因为最大数据是,注意用long,还有一点是,因为数据太大了,矩阵乘法中,加法加完也要取模。
import java.io.*;
import java.util.*;
public class Main {
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
static final int mod = 1000000007;
public static void mut_mul(long[][] a, long[][] b, long[][] c){
long[][] t = {{0, 0}, {0, 0}}; // 中间数组
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
for(int k = 0; k < 2; k++)
t[i][j] = (t[i][j] + (a[i][k]*b[k][j]) % mod)%mod;
for(int i = 0; i < 2; i++) c[i] = Arrays.copyOf(t[i], 2);
}
public static void qmi(long[][] a, long b, long[][] c){// a = a * c^b
while(b != 0){
if((b & 1) == 1){
mut_mul(a, c, a);
}
b >>= 1;
mut_mul(c, c, c);
}
}
public static void main(String[] args) throws NumberFormatException, IOException{
long n;
n = Long.valueOf(in.readLine());
long[][] a = {{1,1}, {0,0}}; //初始A和B都是1, 然后因为矩阵乘法都是二维数组,所以这里也将其写作二维数组。
long[][] c = {{1, 1}, {1, 0}};// 关系数组
qmi(a, n-1, c);
out.write(a[0][0] + "\n");
out.flush();
}
}