【矩阵运算+模板】

矩阵运算是属于线性代数里的一个重要内容,上学期学完后只觉得矩阵能解线性方程,不过高中的时候听说过矩阵能优化常系数递推以及将座标上的点作线性变换,于是找了些资料研究了一下,并把许多经典题以及HDU shǎ崽大牛总结的矩阵乘法的题目[1][2]和开设的矩阵乘法DIY Contest给做完了,感觉收获颇丰。
    一个矩阵就是一个二维数组,为了方便声明多个矩阵,我们一般会将矩阵封装一个类或定义一个矩阵的结构体,我采用的是后者:

    最特殊的矩阵应该就是单位矩阵E了,它的对角线的元素为1,非对角线元素为0。

    若A为n×p矩阵,B为p×m矩阵,则它们的乘积AB(有时记做A·B)将是一个n×m矩阵。其乘积矩阵AB的第i行第j列的元素为:

    一般矩阵乘法采用朴素的O(n^3)的算法:


矩阵加法就是简单地将对应的位置的两个矩阵的元素相加:



    在ACM的题目中,我们一般考虑的是n阶方阵之间的乘法以及n阶方阵与n维向量(把向量看成n×1的矩阵)的乘法。矩阵乘法最重要的性质就是满足结合律,同时它另一个很重要的性质就是满足交换率,这保证了矩阵的幂运算满足快速幂取模(A^k % MOD)算法:

假设k = 27,则k的二进制表示为11011,所以

,可以看出:k的二进制的每一位矩阵A都要平方,在k二进制为1的位:末矩阵×平方后的A,在k二进制为0的位则末矩阵×E(单位矩阵),即不变。代码如下:



许多题目还要求S = A + A2 + A3 + … + Ak.。其实再作一次二分即可:只需计算log(n)个A的幂即可。

    矩阵在ACM里用处最大的就是加速常系数递推方程的计算,最经典的例子就是Fibonacci数列,如果普通的递推,计算第n项复杂度为o(n),显然对于10^9左右的数据就力不从心了。如果用矩阵快速幂递推的话,计算第n项的复杂度为o(k3*log(n)),对应一般的题目已经绰绰有余了,有的题目可以根据矩阵的特殊性进行优化,可以达到o(k2*log(n))。

    由F[n] = F[n-1] + F[n-2],常系数递推式右边有两项,所以向量和矩阵的规格都为2。容易如下递推:


    实现计算Fibonacci数列第k项的代码如下:




转自http://hi.baidu.com/matrush/blog/item/ed9e7f979287ac037af480c7.html

附上自己的模板:

/**
*注意:这里只考虐方阵情况
**/
int n;//方阵维数
struct Mat{
    int mat[N][N];
}E;
//初始化单位矩阵
Mat init(){
    Mat E;
    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            if(i == j)
            E.mat[i][i] = 1;
            else
            E.mat[i][j] = 0;
        }
    }
    return E;
}
//重载乘法
Mat operator *(Mat a,Mat b){
    Mat c;
    memset(c.mat,0,sizeof(Mat));
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            for(int k = 0; k < n; k++){
                if(a.mat[i][k] && b.mat[k][j]){
                    c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
                }
            }
        }
    }
    return c;
}
//重载加法
Mat operator +(Mat a,Mat b){
    Mat c;
    memset(c.mat,0,sizeof(Mat));
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            c.mat[i][j] = a.mat[i][j] + b.mat[i][j];
        }
    }
    return c;
}
//矩阵快速幂
Mat operator ^(Mat A,int x){
    Mat c;
    c = init();
    for(; x ; x >>= 1){
        if(x&1){
            c = c*A;
        }
        A = A*A;
    }
    return c;
}
//求S = A + A^2 + A^3 + … + A^k
Mat sum(Mat A,int x){
    if(x == 1){
        return A;
    }
    if(x&1){
        return (A^x)+sum(A,x - 1);
    }
    else{
        return sum(A,x/2) + ((A^(x/2)) + E);
    }
}
//求Fibonacci数列第x项(1,1,2,3,5,8....)
int Fib(int x){
    if(x == 1 || x == 2)return 1;
    Mat EE;
    EE.mat[0][0] = 0;
    EE.mat[0][1] = 1;
    EE.mat[1][0] = 1;
    EE.mat[1][1] = 1;
    int beg[2] = {1,
                  1};
    EE = EE^(x - 2);
    int s1 = EE.mat[0][0]*beg[0] + EE.mat[0][1]*beg[1];
    int s2 = EE.mat[1][0]*beg[0] + EE.mat[1][1]*beg[1];
    return s2;
}

另一个模板,使用范围更广

struct Mat{
    int n,m;
    int mat[5][5];
    Mat(){
        memset(mat,0,sizeof(mat));
        n = m = 5;
    };
};
Mat operator *(Mat a,Mat b){
    Mat c;
    c = Mat();
    c.n = a.n,c.m = b.m;
    for(int i=1;i<=a.n;i++){
        for(int j=1;j<=b.m;j++){
            for(int k=1;k<=a.m;k++){
                    c.mat[i][j] += (a.mat[i][k]*b.mat[k][j])%MOD;
                    c.mat[i][j] %= MOD;
            }
        }
    }
    return c;
}
Mat operator +(Mat a,Mat b){
    Mat c;
    c = Mat();
    c.n = a.n,c.m = a.m;
    for(int i=1;i<=a.n;i++){
        for(int j=1;j<=a.m;j++){
            c.mat[i][j] = a.mat[i][j]+b.mat[i][j];
        }
    }
    return c;
}
Mat operator ^(Mat a,int k){
    Mat c;
    int i,j;
    c = Mat();
    c.n = a.n,c.m = a.n;
    for(i=1;i<=a.n;i++)c.mat[i][i] = 1;
    while(k){
        if(k&1){
            c = c*a;
        }
        a = a*a;
        k>>=1;
    }
    return c;
}
void out(Mat a){
    int i,j;
    for(i=1;i<=a.n;i++){
        for(j=1;j<=a.m;j++){
            cout<<a.mat[i][j]<<" ";
        }
        cout<<endl;
    }
}



















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章