• 「算法笔记」线性代数


    一、行列式

    1. 定义

    二阶行列式:( egin{vmatrix} a_{11} & a_{12}\ a_{21} & a_{22} end{vmatrix} =a_{11}a_{22}-a_{21}a_{12} )。即主对角线的乘积减去副对角线的乘积。

    三阶行列式:三阶行列式的计算方法与二阶行列式类似。

    ( egin{vmatrix} a_{11} & a_{12} & a_{13}\ a_{21} & a_{22} & a_{23}\ a_{31} & a_{32} & a_{33} end{vmatrix}=a_{11}a_{22}a_{33}+a_{12}a_{23}a_{31}+a_{13}a_{21}a_{32}-a_{13}a_{22}a_{31}-a_{12}a_{21}a_{33}-a_{11}a_{23}a_{32} )

    (n) 阶行列式:

    (n^2) 个元素构成的 (n) 阶行列式为:

    ( left| a_{ij} ight|_n= egin{vmatrix} a_{11} & a_{12} & cdots & a_{1n} \ a_{21} & a_{22} & cdots & a_{2n} \ vdots & vdots & ddots & vdots \ a_{n1} & a_{n2} & cdots & a_{nn} end{vmatrix} =sumlimits_{j_1 j_2 cdots j_n}(-1)^{r(j_1 j_2 cdots j_n)}a_{1j_1}a_{2j_2}cdots a_{nj_n} )

    其中 (j_1 j_2 cdots j_n)(1,2,cdots,n) 的一个排列。

    (r(j_1 j_2 cdots j_n)) 是排列的逆序对数量。

    2. 性质

    (n) 阶行列式为 (D=left| a_{ij} ight| _n),称行列式 (left| a_{ji} ight| _n)(行与列交换一下,即 (a_{ij}) 变为 (a_{ji}))为 (D) 的转置行列式,记作 (D^T)

    一些性质:

    • (D^T=D)

    • 交换行列式 (D) 中任意两行得到 (D_1),则 (D_1=-D)

    • 行列式 (D) 中某一行都乘上 (k) 得到 (D_1),则 (D_1=kD)

    • 行列式 (D) 中的某一行加上另一行的 (c) 倍(对应相加,其中 (c) 为一个常数)得到 (D_1),则 (D_1=D)

    3. 代数余子式

    余子式与代数余子式:

    (n)(n>1)) 阶行列式 (D=|a_{ij}|) 中,划去元素 (a_{ij}) 所在的第 (i) 行和第 (j) 列,余下的元素按原来顺序组成的 (n-1) 阶行列式,称为 (D) 中元素 (a_{ij}) 的余子式,即为 (M_{ij})

    (a_{ij}) 的余子式 (M_{ij}),在它前面添加符号 ((-1)^{i+j}) 后,称为 (a_{ij})(D) 中的代数余子式,记为 (A_{ij}),即 (A_{ij}=(-1)^{i+j} M_{ij})

    (n) 阶行列式 (D=|a_{ij}|) 等于它的任意一行(列)中各元素与其对应的代数余子式乘积的和,即 (D=a_{i1}A_{i1}+a_{i2}A_{i2}+cdots+a_{in}A_{in}) 或 (D=a_{1j}A_{1j}+a_{2j}A_{2j}+cdots+a_{nj}A_{nj})(i,j=1,2,cdots,n))。

    举个栗子:

    (D=egin{vmatrix}1 & 2 & 3\0 & 2 & 0\2 & 3 & 1end{vmatrix}=0cdot A_{21}+2cdot A_{22}+0cdot A_{23})
    (=2cdot A_{22}=2cdot (-1)^{2+2}egin{vmatrix}1 & 3\2 & 1end{vmatrix}=2 imes (-5)=-10)

     (k) 阶子式的余子式与代数余子式:

    (n) 阶行列式 (D) 中划去任意选定的 (k) 行、(k) 列后((0<k<n)),位于这些行和列交叉处的 (k^2) 个元素按原来顺序组成的 (k) 阶行列式 (M),称为 (M) 是行列式 (D) 的一个 (k) 阶子式。

    (n) 阶行列式 (D) 中划去任意选定的 (k) 行、(k) 列后((0<k<n)),余下的元素按原来顺序组成的 (n-k) 阶行列式 (N),称为 (N) 是 (k) 阶子式 (M) 的余子式。

    如果 (k) 阶子式 (M) 在行列式 (D) 中的行和列的标号分别为 (i_1,i_2,cdots,i_k)(j_1,j_2,cdots,j_k),则在 (M) 的余子式 (N) 前面添加符号 ((-1)^{(i_1+i_2+cdots+i_k)+(j_1+j_2+cdots+j_k)}) 后,所得到的 (n-k) 阶行列式,称为行列式 (D)(k) 阶子式 (M) 的代数余子式,记作 (A)。即 (A=(-1)^{(i_1+i_2+cdots+i_k)+(j_1+j_2+cdots+j_k)}N)。

    (主子式:去掉的行和列编号相同。)

    4. 范德蒙德行列式

    (D_n=egin{vmatrix} 1 & 1 & cdots & 1 \ x_1 & x_2 & cdots & x_n \ x_1^2 & x_2^2 & cdots & x_n^2\ vdots & vdots & ddots & vdots \ x_1^{n-1} & x_2^{n-1} & cdots & x_n^{n-1} end{vmatrix} =prodlimits_{1leq i<jleq n}(x_j-x_i))

    用数学归纳法证明。

    (n=2) 时,(D_2=egin{vmatrix} 1 & 1 \ x_1 & x_2end{vmatrix}=x_2-x_1),显然结论成立。

    假设该结论对 (n-1) 阶范德蒙行列式成立,即:

    (D_{n-1}=egin{vmatrix} 1 & 1 & cdots & 1 \ x_1 & x_2 & cdots & x_{n-1} \ x_1^2 & x_2^2 & cdots & x_{n-1}^2\ vdots & vdots & ddots & vdots \ x_1^{n-2} & x_2^{n-2} & cdots & x_{n-1}^{n-2} end{vmatrix}=prodlimits_{1leq i<jleq {n-1}}(x_j-x_i))

    考虑 (n) 阶范德蒙行列式的情形。

    从第 (n) 行开始,自下而上地依次让每一行减去它上一行的 (x_n) 倍。

    (D_n=egin{vmatrix} 1 & 1 & cdots & 1 \ x_1-x_n & x_2-x_n & cdots & 0 \ x_1(x_1-x_n) & x_2(x_2-x_n) & cdots & 0\ vdots & vdots & ddots & vdots \ x_1^{n-2}(x_1-x_n) & x_2^{n-2}(x_2-x_n) & cdots & 0 end{vmatrix})
    (=(-1)^{n+1}egin{vmatrix}x_1-x_n & x_2-x_n & cdots & x_{n-1}-x_n \ x_1(x_1-x_n) & x_2(x_2-x_n) & cdots & x_{n-1}(x_{n-1}-x_n)\ vdots & vdots & ddots & vdots \ x_1^{n-2}(x_1-x_n) & x_2^{n-2}(x_2-x_n) & cdots & x_{n-1}^{n-2}(x_{n-1}-x_n) end{vmatrix})

    注意到行列式的一个性质:行列式 (D) 中某一行都乘上 (k) 得到 (D_1),则 (D_1=kD)。列也是如此。那么就可以提取每一列的公因式。

    (D_n=(-1)^{n+1}(x_1-x_n)(x_2-x_n)cdots (x_{n-1}-x_n)egin{vmatrix}1 & 1 & cdots & 1 \ x_1 & x_2 & cdots & x_{n-1}\ vdots & vdots & ddots & vdots \ x_1^{n-2} & x_2^{n-2} & cdots & x_{n-1}^{n-2} end{vmatrix})
    (=(-1)^{n+1}(x_1-x_n)(x_2-x_n)cdots (x_{n-1}-x_n)D_{n-1})
    (=(-1)^{n+1}(x_1-x_n)(x_2-x_n)cdots (x_{n-1}-x_n)prodlimits_{1leq i<jleq {n-1}}(x_j-x_i))
    (=prodlimits_{1leq i<jleq n}(x_j-x_i))

    二. 矩阵

    1. 定义

    (m imes n) 个数排成的 (m)(n) 列的矩阵数表

    (egin{bmatrix}a_{11}&a_{12}&cdots&a_{1n}\a_{21}&a_{22}&cdots&a_{2n}\vdots&vdots&ddots&vdots\a_{m1}&a_{m2}&cdots&a_{mn}end{bmatrix})

    称为一个 (m imes n) 的矩阵。

    矩阵的运算:加法、数乘、乘法、转置。此处略。

    2. 伴随矩阵

    (A=(a_{ij})_n)(n) 阶矩阵,(A_{ij})(A) 中元素 (a_{ij}) 的代数余子式,(i,j=1,2,cdots,n),则称矩阵

    (egin{bmatrix}a_{11}&a_{12}&cdots&a_{1n}\a_{21}&a_{22}&cdots&a_{2n}\vdots&vdots&ddots&vdots\a_{n1}&a_{n2}&cdots&a_{nn}end{bmatrix})

    (A) 的伴随矩阵,记作 (A^*)。可以证明 (AA^*=A^*A=left|A ight|E)

    3. 求逆矩阵

    设 (A) 是 (n) 阶矩阵,若存在 (n) 阶矩阵 (B),使得 (AB=BA=E),则称矩阵 (A) 可逆,(B) 是 (A) 的逆矩阵,记作 (B=A^{-1})。

    (A^{-1}=frac{1}{|A|}A^*)

    构造一个 增广矩阵 (W=[Amid E]),对 (W) 初等变换,变成 (W=[Emid B]),可以确定 (A^{-1}=B)

    初等变换包括:

    • 交换的矩阵的两行(列)。

    • 矩阵某一行(列)的元素乘同一个不等于 (0) 的数。

    • 矩阵某一行(列)的元素加上另一行(列)对应元素的相同倍数。

    三、矩阵加速递推

    HDU 4549 M斐波那契数列

    题目大意:(M) 斐波那契数列 (F_n) 是一种整数数列。(F_0=a,F_1=b,F_n=F_{n-1} imes F_{n-2})(n>1))。给定 (a,b,n),求 (F_n) 的值。对 (10^9+7) 取模。

    Solution:

    (F_0=a,F_1=b,F_n=F_{n-1} imes F_{n-2}),那么 (F_2=ab,F_3=ab^2,F_4=a^2b^3,F_5=a^3b^5,F_6=a^5b^8,cdots,F_n=a^{f_{n-1} }b^{f_n})。其中 (f_i) 为斐波那契数列的第 (i) 项。问题转化为如何快速求斐波那契数列。

    考虑构造出转移矩阵 (egin{bmatrix}a_1 & a_2\a_3 & a_4end{bmatrix}),使得:

    (egin{bmatrix}f_{n-1} & f_nend{bmatrix} imes egin{bmatrix}a_1 & a_2\a_3 & a_4end{bmatrix}=egin{bmatrix}f_n & f_{n+1}end{bmatrix})

    (f_{n-1} imes a_1+f_n imes a_3=f_n)(f_{n-1} imes a_2+f_n imes a_4=f_{n+1}),显然 (a_1=0,a_2=1,a_3=1,a_4=1)

    即转移矩阵为 (egin{bmatrix}0 & 1\1 & 1end{bmatrix})

    (egin{bmatrix}f_n & f_{n+1}end{bmatrix}=egin{bmatrix}f_{n-1} & f_nend{bmatrix} imes egin{bmatrix}0 & 1\1 & 1end{bmatrix}=egin{bmatrix}f_{n-2} & f_{n-1}end{bmatrix} imes egin{bmatrix}0 & 1\1 & 1end{bmatrix}^2=egin{bmatrix}f_0 & f_1end{bmatrix} imes egin{bmatrix}0 & 1\1 & 1end{bmatrix}^n)

    (F_n=(a^{f_{n-1} }mod m) imes(b^{f_n}mod m)),其中 (m=10^9+7)。则 (f_n)(f_{n-1}) 会非常大,需要用费马小定理降幂。

    (a^pmod m=a^{pmod (m-1)})。所以 (F_n=a^{(f_{n-1} mod (m-1))}b^{(f_nmod (m-1))})

    #include<bits/stdc++.h>
    #define int long long 
    using namespace std;
    const int N=2,mod=1e9+7;
    int x,y,n,f[N][N],k1,k2;
    void mul(int x[N][N],int y[N][N]){    //矩阵乘法 
        int c[N][N];
        memset(c,0,sizeof(c));
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                for(int k=0;k<N;k++)
                    c[i][j]=(c[i][j]+x[i][k]*y[k][j])%(mod-1);
        memcpy(x,c,sizeof(c));
    }
    int ksm(int x,int n,int mod){    //快速幂 
        int ans=mod!=1;
        for(x%=mod;n;n>>=1,x=x*x%mod)
            if(n&1) ans=ans*x%mod;
        return ans;
    }
    signed main(){
        while(~scanf("%lld%lld%lld",&x,&y,&n)){
            int a[N][N]={{0,1},{1,1}};    //转移矩阵 
            memset(f,0,sizeof(f));
            for(int i=0;i<N;i++) f[i][i]=1;    //构造单位矩阵。单位矩阵起着特殊的作用,如同数的乘法中的 1。
            for(;n;n>>=1,mul(a,a))    //矩阵快速幂 
                if(n&1) mul(f,a);
            k1=f[0][0],k2=f[0][1];    //k1=f[n],k2=f[n+1]。其中 f[i] 为斐波那契数列第 i 项。 
            printf("%lld
    ",ksm(x,k1,mod)*ksm(y,k2,mod)%mod);
        }
        return 0;
    }

    多阶线性递推

    例如 (f_1=f_2=0)(f_n=7f_{n-1}+6f_{n-2}+5n+4 imes 3^n)。这里直接给出转移:

    (egin{bmatrix}f_n & f_{n-1} & n & 3^n &1end{bmatrix} imes egin{bmatrix}7 & 1 & 0 & 0 & 0\6 & 0 & 0 & 0 & 0\5 & 0 & 1 & 0 & 0\12 & 0 & 0 & 3 & 0\5 & 0 & 1 & 0 & 1end{bmatrix}=egin{bmatrix}f_{n+1} & f_n & n+1 & 3^{n+1} &1end{bmatrix})

    四、高斯消元

    1. 基本思想

    高斯消元是一种求解线性方程组的方法。求解这种方程组的步骤可概括成对 增广矩阵 的三类操作:

    • 用一个非零的数乘某一行。

    • 把其中一行的若干倍加到另一行上。

    • 交换两行的位置。

    我们把这三类操作成为矩阵的“初等行变换”。同理,我们也可以定义矩阵的“初等列变换”。

    通过初等行变换把增广矩阵变为简化阶梯形矩阵的线性方程组求解算法就是高斯消元算法。高斯消元的算法思想就是,对于每个未知量 (x_i),找到一个 (x_i) 的系数非零,但 (x_1sim x_{i-1}) 的系数都是零的方程(当然也有可能找不到),然后用初等行变换把其他方程的 (x_i) 的系数全部消成零。

    另外,在高斯消元的过程中,可能会遇到各种各样的特殊情形。

    当消元后出现了 (0=b) 这样的方程,其中 (b) 是一个非零常数,则说明原方程组无解。

    当消元后某一方程有不只一个系数非零,那么原方程组有无数解。

    2. 举个栗子

    (left{egin{aligned} x_1+2x_2-x_3&=-6 otag \ 2x_1+x_2-3x_3&=-9 otag \ -x_1-x_2+2x_3&=7 otag end{aligned} ight.)

    先把它写成增广矩阵。

    (left{egin{aligned} x_1+2x_2-x_3&=-6 otag \ 2x_1+x_2-3x_3&=-9 otag \ -x_1-x_2+2x_3&=7 otag end{aligned} ight. Rightarrow egin{bmatrix}egin{array}{ccc|c}1 & 2 & -1 & 3\0 & -3 & -1 & 3\-1 & -1 & 2 & 7end{array}end{bmatrix})

    然后用若干次初等行变换求解上面的方程组,过程如下:

    (egin{bmatrix}1 & 2 & -1 & -6\2 & 1 & -3 & -9\-1 & -1 & 2 & 7end{bmatrix}stackrel{r_2-2r_1}{Longrightarrow}egin{bmatrix}1 & 2 & -1 & -6\0 & -3 & -1 & 3\-1 & -1 & 2 & 7end{bmatrix}stackrel{r_3+r_1}{Longrightarrow}egin{bmatrix}1 & 2 & -1 & -6\0 & -3 & -1 & 3\0 & 1 & 1 & 1end{bmatrix})

    (stackrel{swap(r_2,r_3)}{Longrightarrow}egin{bmatrix}1 & 2 & -1 & -6\0 & 1 & 1 & 1\0 & -3 & -1 & 3end{bmatrix} stackrel{r_3+3r_2}{Longrightarrow}egin{bmatrix}1 & 2 & -1 & -6\0 & 1 & 1 & 1\0 & 0 & 2 & 6end{bmatrix} stackrel{r_3 imes 0.5}{Longrightarrow}egin{bmatrix}1 & 2 & -1 & -6\0 & 1 & 1 & 1\0 & 0 & 1 & 3end{bmatrix})

    最后得到的矩阵被称为“阶梯形矩阵”,它的系数矩阵部分被称为“上三角矩阵”。这个矩阵表达的信息是:

    (egin{bmatrix}egin{array}{ccc|c}1 & 2 & -1 & -6\0 & 1 & 1 & 1\0 & 0 & 1 & 3end{array}end{bmatrix} Rightarrow left{egin{aligned} x_1+2x_2-x_3&=-6 otag \ x_2+x_3&=1 otag \ x_3&=3 otag end{aligned} ight.)

    因此,我们已经知道了最后一个未知量的值,从下往上依次代回方程组,即可得到每个未知量的解。事实上,该矩阵也可以进一步化简:

    (egin{bmatrix}1 & 2 & -1 & -6\0 & 1 & 1 & 1\0 & 0 & 1 & 3end{bmatrix}stackrel{r_1+r_3,r_2-r_3}{Longrightarrow} egin{bmatrix}1 & 2 & 0 & -3\0 & 1 & 0 & -2\0 & 0 & 1 & 3end{bmatrix}stackrel{r_1-2r_2}{Longrightarrow} egin{bmatrix}egin{array}{ccc|c}1 & 0 & 0 & 1\0 & 1 & 0 & -2\0 & 0 & 1 & 3end{array}end{bmatrix})

    最后得到的矩阵被称为“简化阶梯形矩阵”,它的系数矩阵部分是一个“对角矩阵”。该矩阵已经直接给出了方程的解。

    //Luogu P3389
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=110;
    int n;
    double a[N][N];
    signed main(){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n+1;j++)
                scanf("%lf",&a[i][j]);    //增广矩阵 
        for(int i=1;i<=n;i++){    //消第 i 个元
            int p=i;
            for(int j=i;j<=n;j++)    //找到 x[i] 的系数不为 0 的一个方程
                if(fabs(a[j][i])>1e-8){p=j;break;}
            for(int j=1;j<=n+1;j++)
                swap(a[i][j],a[p][j]);
            if(fabs(a[i][i])<1e-8) puts("No Solution"),exit(0);    //不存在唯一解 
            for(int j=1;j<=n;j++){    //消去其他方程的 x[i] 的系数
                if(i==j) continue;
                double x=a[j][i]/a[i][i];
                for(int k=i;k<=n+1;k++)
                    a[j][k]-=a[i][k]*x;
            }
        }
        for(int i=1;i<=n;i++)
            printf("%.2lf
    ",a[i][n+1]/a[i][i]);
        return 0;
    } 

    五、生成树计数

    1. 前置概念

    (G) 的度数矩阵 (D[G]) 是一个 (n imes n) 的矩阵,并且满足:当 (i eq j) 时,(d_{ij}=0);当 (i=j) 时,(d_{ij}) 等于 (v_i) 的度数。

    (G) 的邻接矩阵 (A[G]) 也是一个 (n imes n) 的矩阵,并且满足:如果 (v_i)(v_j) 之间有边直接相连,则 (a_{ij}=1),否则为 (0)

    定义 (G) 的 Kirchhoff 矩阵(也称为拉普拉斯算子)(C[G])(C[G]=D[G]-A[G])

    2. Matrix-Tree 定理

    (G) 的所有不同的生成树的个数等于其 Kirchhoff 矩阵 (C[G]) 任何一个 (n-1) 阶主子式的行列式的绝对值。

    证明省略。但在证明中我们用到了一个矩阵,称为关联矩阵。

    关联矩阵是一个 (n)(m) 列的矩阵,行对应点而列对应边。

    关联矩阵满足,如果存在一条边 (e={v_i,v_j}),那在 (e) 所对应的列中,(v_i)(v_j) 所对应的那两行,一个为 (1)、另一个为 (-1),其他的均为 (0)。至于哪个是 (1) 哪个是 (-1) 并不重要。

    我们令关联矩阵为 (B),考虑 (BB^T),可以发现 (C=BB^T)

    //SP104 HIGH - Highways
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=20;
    int t,n,m,x,y;
    double a[N][N],ans; 
    void Gauss(int n){    //高斯消元 
        for(int i=1;i<=n;i++){
            int p=i;
            for(int j=i;j<=n;j++)
                if(fabs(a[j][i])>1e-8){p=j;break;}
            for(int j=1;j<=n+1;j++)
                swap(a[i][j],a[p][j]);
            if(fabs(a[i][i])<1e-8) return ;
            for(int j=1;j<=n;j++){
                if(i==j) continue;
                double x=a[j][i]/a[i][i];
                for(int k=i;k<=n+1;k++)
                    a[j][k]-=a[i][k]*x;
            }
        }
    }
    signed main(){
        scanf("%lld",&t);
        while(t--){
            memset(a,0,sizeof(a)),ans=1;
            scanf("%lld%lld",&n,&m);
            for(int i=1;i<=m;i++){
                scanf("%lld%lld",&x,&y);
                a[x][x]++,a[y][y]++;
                a[x][y]--,a[y][x]--;    //Kirchhoff 矩阵 
            }
            n--,Gauss(n);    //利用高斯消元将 Kirchhoff 矩阵消为对角矩阵
            for(int i=1;i<=n;i++)
                ans=ans*a[i][i]; 
            printf("%.0lf
    ",fabs(ans));    //高斯消元后,由于已经消成了对角矩阵,所以对角线乘积的绝对值就是答案
        } 
        return 0;  
    }  

    六、习题

    矩阵加速递推:

    • BZOJ 4887「TJOI 2007」可乐
    • LOJ 6208 树上询问(这个代码 过了是什么情况 ¿)

    高斯消元:

    • HDU 2408 String Equations
    • BZOJ 3143「HNOI 2013」游走

    生成树计数:

    • HDU 4408 Minimum Spanning Tree
    • HDU 6836 Expectation
  • 相关阅读:
    [转]常用数字处理算法的Verilog实现
    [转]Linux必学的60个命令
    [转]4位超前进位加法器代码及原理
    [转]FPGA & Verilog开发经验若干
    [转]Verilog中parameter和define的区别
    [转]VIM标记 mark 详解
    关于获取服务的需求列表
    Office 2007 探索之路 Outlook
    利用反射建立单一调用的WebService
    寻宝记
  • 原文地址:https://www.cnblogs.com/maoyiting/p/13743010.html
Copyright © 2020-2023  润新知