• 【笔记】矩阵


    矩阵

    前置知识

    • 定义

    基本概念 : 由 (n imes m) 个数排成 (m)(n) 列的矩形的数表,称为一个 (m imes n) 的矩阵,记作 (A)。其中 (a_{i,j}) 表示第 (i) 行第 (j) 列的元素。

    • 0 矩阵

    对于每个矩阵中的元素 (a_{i,j}) 都为 0,这个矩阵因此也可表示为 (0_{n,m})

    • 单位矩阵

    单位矩阵必须保证 (n = m),否则就不是单位矩阵,单位矩阵只有对角线上的数为 1,其余的数都为 0。

    举个例子 :

    [egin{bmatrix} 1 & 0 & 0 & 0 \ 0 & 1 & 0 & 0 \ 0 & 0 & 1 & 0 \ 0 & 0 & 0 & 1 \ end{bmatrix} ]

    这就是个单位矩阵,可以表示为 (I_{n}),单位矩阵的作用相当于我们运算中的 1。用它来乘以任意数都为本身。

    • 其他的一些矩阵

    好像一般用不到 ?

    对角矩阵,纯量矩阵,上三角矩阵,下三角矩阵,对称矩阵,反对称矩阵。

    矩阵运算

    • 加减法运算

    对于相加和相减的两个矩阵,必须保证两个矩阵大小相同才能做矩阵加减法运算。

    加减法运算的两个矩阵直接对应位置相加或者相减就可以了。

    举个例子 :

    (egin{bmatrix} a_{1,1} & cdots & a_{1,m} \ vdots & ddots & vdots \ a_{n,1} & cdots & a_{n,m} \ end{bmatrix}) + (egin{bmatrix} b_{1,1} & cdots & b_{1,m} \ vdots & ddots & vdots \ b_{n,1} & cdots & b_{n,m} \ end{bmatrix}) = (egin{bmatrix} a_{1,1} + b_{1,1} & cdots & a_{1,m} + b_{1,m} \ vdots & ddots & vdots \ a_{n,1} + b_{n,1} & cdots & a_{n,m} + b_{n,m}\ end{bmatrix})

    是不是很好理解 /cy

    矩阵的加减法满足交换律和结合律。

    • 数乘运算

    (k imes) (egin{bmatrix} a_{1,1} & cdots & a_{1,m} \ vdots & ddots & vdots \ a_{n,1} & cdots & a_{n,m} \ end{bmatrix}) = (egin{bmatrix} k imes a_{1,1} & cdots & k imes a_{1,m} \ vdots & ddots & vdots \ k imes a_{n,1} & cdots & k imes a_{n,m} \ end{bmatrix})

    • 矩阵乘法

    定义 : 矩阵的乘法

    如果要使用矩阵乘法,两个矩阵必须是其中的一个矩阵的行数等与另一个矩阵的列数,即 (A_{n,q})(B_{q,m}) 可以进行矩阵乘法,得出的矩阵的行数是第一个矩阵的行数,矩阵的列数是第二个矩阵的列数。

    我们设 (A = (a_{i,j})_{m imes r},B = (b_{i,j})_{r imes n}),则矩阵 (C = (c_{i,j})_{m,n}),其中 (c_{i,j} = a_{i,1}b_{1,j} + a_{i,2} b_{2,j} + cdots + a_{i,r} b_{r,j})

    记作 (C = A * B)

    举个例子 :

    我们设一个 (A_{2,2}),一个 (B_{2,3}),我们将会的到一个 (C_{2,3}) 的矩阵。

    $egin{bmatrix}
    1 & 2
    3 & 4
    end{bmatrix} imes $$egin{bmatrix}
    1 & 2 & 3
    4 & 5 & 6
    end{bmatrix} =$ (egin{bmatrix} 9 & 12 & 15 \ 19 & 26 & 33 \ end{bmatrix})

    (C_{i,j} = sum) 第一个矩阵的第 (i) 行的每个数与第二个矩阵的第 (j) 列每个数相乘(一一对应)。

    矩阵乘法的性质

    1. (0_{n,m} A_{m,k} = 0,A_{k,m} 0_{m,n} = 0)
    2. (I_{n,m} A_{m,k} = A,A_{k,m} I_{m,n} = A)
    3. (A(BC) = (AB)C)
    4. (A(B + C) = AB + BC),左分配率律
    5. ((B + C) A = B A + C A),右分配率律

    注意左右两分配率矩阵相乘的顺序不能颠倒。

    矩阵乘法没有交换律!!!

    但是有三种特殊情况,交不交换无所谓。

    (O A = A O = 0)
    (I A = A I = A)
    (A A = A A = A ^ 2)

    下面给出一个矩阵乘法的代码。

    ( ext{problem})Link

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #define int long long
    using namespace std;
    const int Maxk = 110;
    int n,m,p;
    struct Matrix {
      int n1,m1;
      int z[Maxk][Maxk];
      Matrix (){
        n1 = m1 = 0;
        memset(z,0,sizeof z);
      }
    } A , B ; 
    inline int read()
    {
    	int s = 0, f = 0;char ch = getchar();
    	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
    	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
    	return f ? -s : s;
    }
    Matrix operator * (const Matrix &m1,const Matrix &m2)
    {
      Matrix m3;
      m3.n1 = m1.n1;
      m3.m1 = m2.m1;
      for(int i = 1;i <= m3.n1;i ++) 
        for(int k = 1;k <= m1.m1;k ++) 
          for(int j = 1;j <= m2.m1;j ++)
            m3.z[i][j] += m1.z[i][k] * m2.z[k][j];
      return m3;  
    } 
    signed main()
    {
      n = read(),m = read();
      for(int i = 1;i <= n;i ++) 
        for(int j = 1;j <= m;j ++) 
          A.z[i][j] = read();
      p = read();
      for(int i = 1;i <= m;i ++) 
        for(int j = 1;j <= p;j ++)
          B.z[i][j] = read();
      A.n1 = n,A.m1 = m;
      B.n1 = m,B.m1 = p;
      Matrix Ans = A * B;
      for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= p;j ++)  {
          cout << Ans.z[i][j] << " ";
        }
        cout << '
    ';
      }
      return 0;
    }
    
    • 矩阵快速幂

    矩阵快速幂就是把快速幂和矩阵乘法结合起来,没有什么很难的,我们只需要像快速幂那样先初始化一个单位矩阵,之后按照快速幂的形式不断相乘即可。

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #define int long long
    using namespace std;
    const int mod = 1e9 + 7; 
    const int Maxk = 110;
    int n,m;
    struct Matrix {
      int n1,m1;
      int z[Maxk][Maxk];
      Matrix (){
        n1 = m1 = 0;
        memset(z,0,sizeof z);
      }
    } A , E ; 
    inline int read()
    {
    	int s = 0, f = 0;char ch = getchar();
    	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
    	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
    	return f ? -s : s;
    }
    Matrix operator * (const Matrix &m1,const Matrix &m2)
    {
      Matrix m3;
      m3.n1 = m1.n1;
      m3.m1 = m2.m1;
      for(int i = 1;i <= m3.n1;i ++) 
        for(int k = 1;k <= m1.m1;k ++) 
          for(int j = 1;j <= m2.m1;j ++) {
            m3.z[i][j] = m3.z[i][j] + m1.z[i][k] * m2.z[k][j];
            if(m3.z[i][j] >= mod) m3.z[i][j] %= mod;
          }
      return m3;  
    } 
    signed main()
    {
      n = read(),m = read();
      for(int i = 1;i <= n;i ++) 
        for(int j = 1;j <= n;j ++) 
          A.z[i][j] = read();
      A.n1 = n,A.m1 = n;
      for(int i = 1;i <= n;i ++) E.z[i][i] = 1;//初始化单位矩阵
      E.n1 = n,E.m1 = n;
      while(m) {//按照快速幂的方法来乘
        if(m & 1) E = E * A;
        A = A * A;
        m >>= 1; 
      }
      for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= n;j ++) {
          cout << E.z[i][j] << " ";
        } 
        cout << "
    "; 
      } 
      return 0;
    }
    

    矩阵乘法应用 & 矩阵加速

    因为矩阵加减法太简单了,所以基本没有什么题目来考矩阵加减法。

    斐波那契数列加速求解

    ( ext{problem}) : Link

    首先我们知道 (f_{i} = f_{i - 1} + f_{i - 2})

    很多这种递推式都可用矩阵来加速,我们只要找到一个矩阵,保证这个矩阵在不变的情况下就可以来求出下一项,我们就可以矩阵加速求解。

    我们定义 (A) 矩阵为

    [egin{bmatrix} f_{n} & f_{n - 1} end{bmatrix}]

    我们要求的 (C) 矩阵为

    [egin{bmatrix} f_{n + 1} & f_{n} end{bmatrix}]

    我们要找一个 (B) 矩阵,保证在 (B) 矩阵不变的情况下就可以进行矩阵加速。

    如何来找这个矩阵 ?

    首先,我们的 (C) 矩阵有一行两列,所以可以得出,我们的 (B) 矩阵是两行两列,设 (B) 矩阵为 :

    [egin{bmatrix} a & b \ c & d \ end{bmatrix}]

    根据矩阵乘法,显然可以得到 :

    [egin{aligned} f_{n + 1} &= a imes f_{n} + c imes f_{n - 1} \ f_{n} &= b imes f_n + d imes f_{n - 1} \ end{aligned}]

    所以我们很容易的到 (b = 1,d = 0),之后在根据 (f_n = f_{n - 1} + f_{n - 2}),所以 (a = 1,b = 1)

    我们的 (B) 矩阵就可以得出了 :

    [egin{bmatrix} 1 & 1 \ 1 & 0 \ end{bmatrix}]

    之后我们直接套一个快速幂就可以解决了。

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #define int long long
    using namespace std;
    const int mod = 1e9 + 7; 
    const int Maxk = 110;
    int n,m;
    struct Matrix {
      int n1,m1;
      int z[3][3];
      Matrix (){
        n1 = m1 = 0;
        memset(z,0,sizeof z);
      }
    } A ,B,E ; 
    inline int read()
    {
    	int s = 0, f = 0;char ch = getchar();
    	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
    	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
    	return f ? -s : s;
    }
    Matrix operator * (const Matrix &m1,const Matrix &m2)
    {
      Matrix m3;
      m3.n1 = m1.n1;
      m3.m1 = m2.m1;
      for(int i = 1;i <= m3.n1;i ++) 
        for(int k = 1;k <= m1.m1;k ++) 
          for(int j = 1;j <= m2.m1;j ++) {
            m3.z[i][j] = m3.z[i][j] + m1.z[i][k] * m2.z[k][j];
            if(m3.z[i][j] >= mod) m3.z[i][j] %= mod;
          }
      return m3;  
    } 
    signed main()
    {
      n = read();
      if(n == 1 || n == 0) {
        cout << 1 << endl;
        return 0;
      }
      A.z[1][1] = 1,A.z[1][2] = 0;
      A.n1 = 1,A.m1 = 2;
      B.z[1][1] = 1,B.z[1][2] = 1;
      B.z[2][1] = 1,B.z[2][2] = 0;
      B.n1 = 2,B.m1 = 2;
      for(int i = 1;i <= 2;i ++) E.z[i][i] = 1;//初始化单位矩阵
      E.n1 = 2,E.m1 = 2;
      m = n - 1;
      while(m) {
        if(m & 1) E = E * B;
        B = B * B;
        m >>= 1; 
      }
      A = A * E;
      cout << A.z[1][1] % mod;
      return 0;
    }
    

    斐波那契数列求和

    ( ext{problem}) : Link

    似乎和前面差不多,也是推式子。

    我们用 (S_i) 代表从第 (1) 项到第 (i) 项的和,显然 (A) 矩阵和 (C) 矩阵一样设就行了,上面是 (A),下面是 (C)

    [egin{bmatrix} f_{n} & f_{n - 1} & S_n end{bmatrix}]

    [egin{bmatrix} f_{n + 1} & f_{n} & S_{n + 1} end{bmatrix}]

    明显我们要设一个 (B) 矩阵,行列都为 3。

    [egin{bmatrix} a & b & c\ d & e & f \ g & h & i \ end{bmatrix}]

    得出 :

    [egin{aligned} f_{n + 1} &= a imes f_{n} + d imes f_{n - 1} + g imes S_n \ f_{n} &= b imes f_n + e imes f_{n - 1} + h imes S_n \ S_{n + 1} &= c imes f_n + f imes f_{n - 1} + i imes S_n \ end{aligned}]

    所以化简一下就得到了矩阵 (B)

    [egin{bmatrix} 1 & 1 & 1\ 1 & 0 & 1 \ 0 & 0 & 1 \ end{bmatrix}]

    其实还有一种方法 :

    [S_n = f_0 + f_1 + cdots + f_n \ S_{n + 1} = f_0 + f_1 + cdots + f_{n + 1} ]

    [egin{aligned} S_n + S_{n + 1} &= f_0 + f_2 + f_3 + cdots f_{n + 2} \ &= S_{n + 2} - f_0 \ &= S_{n + 2} - 1 end{aligned}]

    注意如果有常数想的情况下要增加一个维度来存常数项。

    [egin{bmatrix} S_n & S_{n - 1} & 1 end{bmatrix}]

    [egin{bmatrix} S_{n + 1} & S_{n} & 1 end{bmatrix}]

    的出的 (B) 矩阵为 :

    [egin{bmatrix} 1 & 1 & 0\ 1 & 0 & 0 \ 1 & 0 & 1 \ end{bmatrix}]

    就不再多阐述了。

    Problem

    由于上课的时候没怎么听懂,先说一下题面。。

    Description

    (n) 个点的图,共有 (m) 条边,且都是单向边。

    (M_{i,j}) 表示从第 (i) 个点到第 (j) 个点有多少条边。

    求从 1 号点走 (t) 条边到第 (n) 号点的方案数。

    (n leq 100,t leq 10 ^ 9)

    Solution

    咕咕咕

    如果要用矩阵乘法加速 (DP),要保证两点。

    • 必须从 (i - 1) 转移到 (i)
    • (M) 不会随着 (i) 的改变而改变,即第二个矩阵无关。
  • 相关阅读:
    ios 常见性能优化
    ios Standard Framework和Umbrella Framework
    lua 中容易忘记的细节
    python md5
    lua table 排序
    centos 图形模式与命令行模式设置
    python 中关于 json 的一些相关操作
    python3 入门基础
    lua 函数
    sublime 自定义配置python开发环境
  • 原文地址:https://www.cnblogs.com/Ti-despair/p/14731233.html
Copyright © 2020-2023  润新知