首先介绍矩阵乘法的本质:
小明今天要做饭,消耗2斤肉,1斤蔬菜。肉每斤20元,蔬菜每斤5元,则一共需多少花费?
这个问题的答案很简单:
我们用向量相乘的方法写出来:
如果小明第二天有另一种做饭的方法,需要消耗1斤肉,4斤蔬菜,那么这两种方法的花费各是多少呢?我们显然需要另算这第二种方法的花费。把这个做饭方式写在第二个矩阵(向量是宽度或长度为1的矩阵)里:
小明家附近还有另一个菜市场,那里肉每斤15元,蔬菜每斤10元。那么,小明如果去这个菜市场,花费又是多少呢(分别计算上述两种做饭方式)?我们把这另外的一种价格写进第一个矩阵里:
小明家附近还有另一个菜市场,那里肉每斤15元,蔬菜每斤10元。那么,小明如果去这个菜市场,花费又是多少呢(分别计算上述两种做饭方式)?我们把这另外的一种价格写进第一个矩阵里:
这样我们看到了一个矩阵乘法的例子。在左边的这个矩阵的每一行,都代表了一种价目表;在右边的矩阵的每一列,都代表了一种做饭方式。那么所有可能的组合所最终产生的花费,则在结果矩阵中表示出来了。小明有一天成为了餐厅大厨,小红做掌柜兼管算账。我们假设物价不变。小红发现,如果今天买10斤肉花了A元,明天买20斤肉就得花2A元。如果买一斤肉要花C元,买1斤菜要花D元,那么买一斤肉和一斤菜就要花(C+D)元。每天小明汇报今日的材料消耗之后,小红便会将材料消耗转为需要花的钱数。如果材料消耗翻倍,花的钱数也翻倍。另外,如果去不同的菜市场,也会得到不同的花钱数量。小明每月送来一张长列表,里面是每日的材料消耗;而经过小红的处理,这张列表会转为每日,在不同的菜市场购买这些材料的花费。材料消耗翻倍,花费也翻倍。我们管这种从材料列表转为开销表的过程,就叫做一个线性映射。这也即是矩阵乘法的意义。最后补充一点。线性代数的引入方式因教材不同而不同。从代数学自身的体系来讲,可能从线性空间引入是相对完备的;但是从一般我们学习知识的理解顺序来讲,从线性方程组引入最为合适。因为只要还记得鸡兔同笼,就很容易理解线性方程组,从而推广到矩阵,然后是线性变换,线性空间。
以上出自: https://www.zhihu.com/question/21351965/answer/31050145
那么矩阵乘法的实现就是模拟手算的过程
for( int i = 0 ; i < n ; i++ )
for( int j = 0 ; j < n ; j++ )
for( int k = 0 ; k < n ; k++)
c[i][j] += a[i][k] * b[k][j];
下面是介绍矩阵快速幂了:
只要知道快速幂的一般实现利用二进制思想将其离散化,那么矩阵快速幂无非就是将一般数字转化成矩阵,算法一致。
矩阵的快速幂问题,也能把它离散化?比如A^19 => (A^16)*(A^2)*(A^1),显然采取这样的方式计算时因子数将是log(n)级别的(原来的因子数是n),不仅这样,因子间也是存在某种联系的,比如A^4能通过(A^2)*(A^2)得到,A^8又能通过(A^4)*(A^4)得到,这点也充分利用了现有的结果作为有利条件。下面举个例子进行说明:
现在要求A^156,而156(10)=10011100(2)
也就有A^156=>(A^4)*(A^8)*(A^16)*(A^128) 考虑到因子间的联系,我们从二进制10011100中的最右端开始计算到最左端。
#include <bits/stdc++.h>
#define MOD 1000000009
using namespace std;
struct Matrix
{
int a[3][3]; //以3*3为例
}origin,res; //res结果数组 origin随机生成的初始数组
void init()
{
printf("Random Matrix:\n");
for(int i=0;i<3;i++) //生成随机数组
{
for(int j=0;j<3;j++)
{
origin.a[i][j]=rand()%10;
printf("%8d",origin.a[i][j]);
}
printf("\n");
}
printf("\n");
memset(res.a,0,sizeof(res.a));
res.a[0][0]=res.a[1][1]=res.a[2][2]=1; //将结果数组初始化为单元数组(和快速幂将保存结果的数初始化为1一样道理)
}
Matrix multiply( Matrix x , Matrix y ){ //矩阵乘法
Matrix temp ;
memset(temp.a,0,sizeof(temp.a));
for( int i = 0 ; i < 3; i++ ){
for( int j = 0 ; j < 3 ; j++ ){
for( int k = 0 ; k < 3 ; k++ ){
temp.a[i][j] += x.a[i][k]*y.a[k][j];
temp.a[i][j] %= MOD;
}
}
}
return temp;
}
void cal(int n){
while(n){ //矩阵快速幂
if( n & 1 ) res = multiply(res,origin);
n>>=1;
origin = multiply(origin,origin);
}
for( int i = 0 ; i < 3; i ++ ){ //打印结果
for( int j = 0 ; j < 3 ; j++ ){
printf("%8d",res.a[i][j]);
}
printf("\n");
}
cout<<endl;
}
int main(){
int n;
while(cin>>n){
init();
cal(n);
}
return 0;
}
输入n次方,计算结果如下:
例题:矩阵快速幂的应用:
1 秒 空间限制: 131072 KB 分值: 0 难度:基础题
收藏
关注
取消关注
斐波那契数列的定义如下:
F(0) = 0
F(1) = 1
F(n) = F(n - 1) + F(n - 2) (n >= 2)
(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, ...)
给出n,求F(n),由于结果很大,输出F(n) % 1000000009的结果即可。
Input
输入1个数n(1 <= n <= 10^18)。
Output
输出F(n) % 1000000009的结果。
Input示例
11
Output示例
89
AC代码:
#include <bits/stdc++.h>
#define LL long long
#define MOD 1000000009
using namespace std;
struct Matrix
{
LL a[2][2];
}origin,res;
Matrix multiply( Matrix x , Matrix y ){
Matrix temp ;
memset(temp.a,0,sizeof(temp.a));
for( int i = 0 ; i < 2; i++ ){
for( int j = 0 ; j < 2 ; j++ ){
for( int k = 0 ; k < 2 ; k++ ){
temp.a[i][j] += (x.a[i][k]%MOD*y.a[k][j]%MOD)%MOD;
temp.a[i][j] %= MOD;
}
}
}
return temp;
}
void cal(LL n){
origin.a[0][0] = origin.a[0][1] = 1;
origin.a[1][0] = 1 ; origin.a[1][1] = 0;
res.a[0][0] = res.a[1][1] = 1;
res.a[0][1] = res.a[1][0] = 0;
while(n){
if( n & 1 ) res = multiply(res,origin);
origin = multiply(origin,origin);
n>>=1;
}
cout<<res.a[0][1]%MOD<<endl;
}
int main(){
LL n;
while(cin>>n){
cal(n);
}
return 0;
}
代码很容易看懂,那么为什么能用矩阵乘法求斐波那契数列呢?
首先我们初始化这样一个矩阵:
1 | 1 |
1 | 0 |
然后res数组是这样的:
F[i-1] |
F[i-2] |
让这两个矩阵相乘 结果就相当于每次下面的替换上面的
F[i-2] + F[i-1] (F[i]) |
F[i-1] |
自然就会得到斐波那契数列第N项f[n]。
并且有这样的一般式: