漸化式の一般項の値を高速に求める
隣接 項間漸化式の一般項 の値を高速に求める手法を最近知ったのでメモをしておく。
一般項を求めるのは、 行列で行列累乗を用いると、 で求めることができる。
ただ、きたまさ法と呼ばれる手法を用いれば、 で求めることができる。
表記など怪しいので、詳しくは参考文献を見ることをお薦めする。
項の初期値と、隣接 項の関係が以下のように与えられているとする。
ただし、 は初期値のベクトル、 は と隣接 項間の関係を書き表すための係数ベクトルである。( 一般的な表記とは異なっているので注意。)
ここで、次のような 次元ベクトル を導入する。
このとき、 は以下のように書けるものと定義する。
のとき、 である。
を求めるために、 を求めることを考える。
が分かっているときに、 が求めることができれば、ダブリングの要領で、 から始めて 回の計算で を求めることができる。
まず、 が分かっているとき、 は で求めることができる。
は 項前から 個取った項との関係を表すものであるので、
のとき、すべての の添え字に をして、
これに の式を代入してまとめると、
すなわち、
となる。
次に、 が分かっているときに、 を求めることを考える。
のとき、すべての の添え字に をすると、
となるので、
の 個のベクトルが分かっていれば、 を求めることができる。
個のベクトルは、 から始めて で求めることができる。
各計算は前述の方法と同様 であるので、合計で となる。
そして、 の計算では、各ベクトルから の係数、 の係数... を足し込めばいいので、これも で求めることができる。
よって、全体で の計算量で、 を求めるための係数ベクトルが求まる。
コード
#include <iostream> #include <vector> using namespace std; template<typename T> struct Kitamasa{ vector<T> a; // 初期値ベクトル vector<T> d; // 係数ベクトル int k; Kitamasa(vector<T>& a, vector<T>& d) : a(a), d(d), k((int)a.size()) {} // a_n の係数ベクトルを求める vector<T> dfs(int64_t n){ if(n == k) return d; vector<T> res(k); if(n & 1 || n < k * 2){ vector<T> x = dfs(n - 1); for(int i = 0; i < k; ++i) res[i] = d[i] * x[k - 1]; for(int i = 0; i + 1 < k; ++i) res[i + 1] += x[i]; } else{ vector<vector<T>> x(k, vector<T>(k)); x[0] = dfs(n >> 1); for(int i = 0; i + 1 < k; ++i){ for(int j = 0; j < k; ++j) x[i + 1][j] = d[j] * x[i][k - 1]; for(int j = 0; j + 1 < k; ++j) x[i + 1][j + 1] += x[i][j]; } for(int i = 0; i < k; ++i){ for(int j = 0; j < k; ++j){ res[j] += x[0][i] * x[i][j]; } } } return res; } // a_n を求める T calc(int64_t n){ vector<T> x = dfs(n); T res = 0; for(int i = 0; i < k; ++i) res += x[i] * a[i]; return res; } };
関連問題
雑記
世の中天才しかいないな…