1. 程式人生 > >斐波那契_矩陣快速冪解法

斐波那契_矩陣快速冪解法

學過矩陣學了矩陣再看斐波那契數列, 秒懂, 結合矩陣快速冪, 加深了一個概念的理解: 矩陣也就是一個基本的計算單位.

矩陣快速冪解法其實就是快速冪+矩陣.

和普通的快速冪有什麼不同? 不同的是基數的型別,快速冪的過程還是一樣的. 同樣的,快速冪結果一般取模, 因為資料實在是太大了. 那麼矩陣快速冪是否也應該取模?

那麼推導一下似乎可以發現,矩陣的每個數都取模p,因為結果其實就是矩陣的某個元素

以[[1, 1], [1, 0]]的冪為n, n = 0時, 結果0, 如果我們取f0 = 0, f1 = 1, 以此類推,那麼n次冪的結果就是右下角的那個數.

同時時間複雜度為O(n log n).

我們來回顧一下快速冪, 是從一次不斷進行倍增, 如果數n 的二進位制形式在某一位上為1,那麼就乘以這一位,否則不乘

先寫一下快速冪程式碼.

#include <iostream>
using namespace std;

int main()
{
    int n, p, ans = 1, val = 2;
    cin >> n >> p;
    while (n) {
        if (n & 1) ans = ans * val % p;
        val *= val;
        n /= 2;
    }
    cout
<< ans; } // OK, 就是這麼簡單.

那麼矩陣快速冪也呼之欲出了.

然而寫了程式碼發現計算過程有誤.(劃掉, 應該是推導有誤,睡個午覺再來看看).
OK, 找出bug來了, 推導沒有錯誤, 是做矩陣乘法的時候幾個下標寫錯了. 需要特別小心

但是發現答案稍微有點問題,看出其實now矩陣不用設定為二維單位矩陣,直接是[1, 0], 他就是答案.

但是非常悲劇的是洛谷上沒有這麼簡單的模板題, 都不給我辛苦學習AC一下的快感.

#include <iostream>
using namespace std;

int main()
{
    int
n, p = (1 << 31); cin >> n; int now[2][1] = {{1}, {0}}, matrix[2][2] = {{1, 1}, {1, 0}}; while (n) { int a, b, c, d; if (n & 1) { // 矩陣相乘. a = matrix[0][0] * now[0][0] + matrix[0][1] * now[1][0]; b = matrix[1][0] * now[0][0] + matrix[1][1] * now[1][0]; now[0][0] = a % p, now[1][0] = b % p; } // 下面是矩陣平方, 有點麻煩. a = matrix[0][0] * matrix[0][0] + matrix[0][1] * matrix[1][0]; b = matrix[0][0] * matrix[0][1] + matrix[0][1] * matrix[1][1]; c = matrix[1][0] * matrix[0][0] + matrix[1][1] * matrix[1][0]; d = matrix[1][0] * matrix[0][1] + matrix[1][1] * matrix[1][1]; //cout << "c = " << c << endl; matrix[0][0] = a % p, matrix[0][1] = b % p, matrix[1][0] = c % p, matrix[1][1] = d % p; n /= 2; //cout << "----------" << endl; //cout << matrix[0][0] << ' ' << matrix[0][1] << endl << matrix[1][0] << ' ' << matrix[1][1] << endl; } cout << now[1][0]; }

再來串矩陣快速冪模板(不只是斐波那契矩陣)

也就是矩陣計算 + 快速冪的組合. O(n**3 + log k)

#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;

const LL mode = 1000000007;

void calc(LL ans[105][105], LL matrix[105][105], LL n)
{
    LL tmp[105][105];
    memset(tmp, 0, sizeof(tmp));
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            for (int k = 1; k <= n; ++k) {
                tmp[i][j] += ans[i][k] * matrix[k][j];
                tmp[i][j] %= mode;
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            ans[i][j] = tmp[i][j];
        }
    }
}

int main()
{
    LL n, k, matrix[105][105], ans[105][105];
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cin >> matrix[i][j];
        }
    }
    // 初始化為單位矩陣. 
    memset(ans, 0, sizeof(ans));
    for (int i = 1; i <= n; ++i) {
        ans[i][i] = 1;
    }
    while (k) {
        if (k & 1) {
            calc(ans, matrix, n);
        }
        calc(matrix, matrix, n);
        k /= 2;
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cout << ans[i][j] << ' ';
        }
        cout << endl;
    }
}
/*
矩陣計算 + 快速冪
*/