矩陣的運算 --- 倍增法(UVA11149 - Power of Matrix)

        昨天我們介紹了矩陣的快速冪(點我),相信大家對於矩陣快速冪都有一定的瞭解。大家也就知道快速冪對於冪運算的迅速,但是當出現了這樣的問題:

        我們就會發現即便矩陣快速冪再快,我們計算和我們都是O(n)的算法。那麼在這個問題上,我們研究問題的重心就從矩陣的冪,轉化爲了矩陣冪的和。光看這個好像也想不出什麼,那麼我們換一個角度來思考這個問題。我們現在是要讓計算變得更快,對吧?那麼怎麼樣才能變得更快呢?時間複雜度的定義,就是從運算的次數來衡量時間的吧。那麼現在我們就有目標了。我們要怎麼減少運算的次數呢?顯然就是儘量避免重複計算,換而言之就是儘可能的找出公共部分。於是我們很容易的得到下列式子:


       現在我們發現公共部分,我們已經找到了,提取公因式:


        同理我們還可以繼續對式子後半部分(A^1 + A^2 + ...  + A^(n/2) )做相同的處理,於是我們驚奇的發現,我們將O(n)的複雜度降低爲 O(log n)的時間複雜度了。

以下是代碼實現:

//Matrix 類,同矩陣快速冪的Matrix類

//倍增法求解a^1 + a^2 + ... + a^n
Matrix slove(const Matrix &a, int n){
    //遞歸終點
    if(n == 1) return a;
    //temp 遞歸表示a^1 + a^2 + ... + a^(n/2)
    Matrix temp = slove(a, n/2);
    //sum 表示 a^1 + a^2 + ... + a^(n/2) + (a^(n/2))*(a^1 + a^2 + ... + a^(n/2))
    Matrix sum = temp + temp*a.pow(n/2);
    //如果當n爲奇數,我們會發現我們的(n/2 + n/2) == n-1
    //於是我們需要補上一項: a^n
    if(n & 1) sum = sum + a.pow(n);
    return sum;
}

       同樣的我們來一道題目練練手:UVA11149 - Power of Matrix

這是一道完全的模板題,完整的代碼如下:

#include <cstdio>
#include <iostream>
#include <limits.h>
#include <algorithm>
using namespace std;

class Matrix{
public:
    int **m;
    int col, row, mod;
    Matrix(int r = 0, int c = 0, int d = INT_MAX);
    Matrix(const Matrix &value);
    ~Matrix();
    Matrix operator + (const Matrix &rht) const;
    Matrix operator * (const Matrix &rht) const;
    Matrix& operator = (const Matrix &rht);
    Matrix pow(int n) const;
};

Matrix::Matrix(int r, int c, int d):row(r), col(c), mod(d){
    m = new int*[row];
    for(int i = 0; i < row; i++){
        m[i] = new int[col];
    }
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            m[i][j] = 0;
        }
    }
}
Matrix::Matrix(const Matrix &value){
    row = value.row;
    col = value.col;
    mod = value.mod;
    m = new int*[row];
    for(int i = 0; i < row; i++){
        m[i] = new int[col];
    }
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            m[i][j] = value.m[i][j];
        }
    }
}
Matrix::~Matrix(){
    for(int i = 0; i < row; i++){
        delete[] m[i];
    }
    delete[] m;
}
Matrix Matrix::operator + (const Matrix &rht) const{
    Matrix temp(row, col, mod);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            temp.m[i][j] = (m[i][j] + rht.m[i][j])%mod;
        }
    }
    return temp;
}
Matrix Matrix::operator * (const Matrix &rht) const{
    Matrix temp(row, rht.col, mod);
    for(int i = 0; i < row; i++){
        for(int k = 0; k < rht.row; k++){
            for(int j = 0; j < rht.col; j++){
                temp.m[i][j] = (temp.m[i][j] + m[i][k]*rht.m[k][j])%mod;
            }
        }
    }
    return temp;
}
Matrix& Matrix::operator = (const Matrix &rht){
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            m[i][j] = rht.m[i][j];
        }
    }
    return *this;
}
//矩陣快速冪
Matrix Matrix::pow(int n) const{
    Matrix a(*this), res(row, col, mod);
    for(int i = 0; i < row; i++){
        res.m[i][i] = 1;
    }
    while(n > 0){
        if(n & 1) res = res * a;
        a = a * a;
        n >>= 1;
    }
    return res;
}
//Matrix 類,同矩陣快速冪的Matrix類

//倍增法求解a^1 + a^2 + ... + a^n
Matrix slove(const Matrix &a, int n){
    //遞歸終點
    if(n == 1) return a;
    //temp 遞歸表示a^1 + a^2 + ... + a^(n/2)
    Matrix temp = slove(a, n/2);
    //sum 表示 a^1 + a^2 + ... + a^(n/2) + (a^(n/2))*(a^1 + a^2 + ... + a^(n/2))
    Matrix sum = temp + temp*a.pow(n/2);
    //如果當n爲奇數,我們會發現我們的(n/2 + n/2) == n-1
    //於是我們需要補上一項: a^n
    if(n & 1) sum = sum + a.pow(n);
    return sum;
}

int main()
{
    int n, k;
    while(scanf("%d%d", &n, &k), n){
        Matrix mat(n, n, 10);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                scanf("%d", &mat.m[i][j]);
                mat.m[i][j] %= 10;
            }
        }
        Matrix ans = slove(mat, k);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                if(j) printf(" ");
                printf("%d", ans.m[i][j]);
            }
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}
(如有錯誤,歡迎指正,轉載請註明出處)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章