斐波那契_矩陣快速冪解法
阿新 • • 發佈:2018-12-24
學過矩陣學了矩陣再看斐波那契數列, 秒懂, 結合矩陣快速冪, 加深了一個概念的理解: 矩陣也就是一個基本的計算單位.
矩陣快速冪解法其實就是快速冪+矩陣.
和普通的快速冪有什麼不同? 不同的是基數的型別,快速冪的過程還是一樣的. 同樣的,快速冪結果一般取模, 因為資料實在是太大了. 那麼矩陣快速冪是否也應該取模?
那麼推導一下似乎可以發現,矩陣的每個數都取模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;
}
}
/*
矩陣計算 + 快速冪
*/