m項間漸化式の第n項までの和を$O(m ^ 2 log n)$で
この記事では項間漸化式の第項までの和をで求める方法について説明します。
@mt_caret がnth Fibonacci number in O(logn)という記事を書いていたのを見て、以前ブログに書こうと思っていて完全に忘却していたネタを思い出したので書きました。
イントロ
項間漸化式の第項
項間の線形漸化式*1
は、次のように行列で表現し、繰り返し二乗法を使って乗を計算することでで求めることができます。
$$A= \begin{pmatrix} c_{m -1} & \cdots & c_{1} & c_{0} \\ 1 & \cdots & 0 & 0 \\ \vdots & \ddots & \vdots & \vdots \\ 0 & \cdots & 1 & 0 \end{pmatrix} $$
$$ \begin{pmatrix} a_{n+m -1} \\ a_{n+m -2} \\ \vdots \\ a_{n} \\ \end{pmatrix} =A \begin{pmatrix} a_{n+m -2} \\ a_{n+m -3} \\ \vdots \\ a_{n -1} \\ \end{pmatrix} =A ^ n \begin{pmatrix} a_{m -1} \\ a_{m -2} \\ \vdots \\ a_{0} \\ \end{pmatrix} $$
このことは、はじめに紹介した記事や蟻本*2で説明されています。
項間漸化式の第項までの和
似たような方法で第項までの和を求めることもできます。
$$ S_i = I + A + \ldots + A ^ {i -1} $$
とすると、ブロック行列を用いて
$$ \begin{pmatrix} A ^ n \\ S_n \\ \end{pmatrix} =\begin{pmatrix} A & 0 \\ I & I \\ \end{pmatrix}\begin{pmatrix} A ^ {n -1} \\ S_{n -1} \\ \end{pmatrix} =\begin{pmatrix} A & 0 \\ I & I \\ \end{pmatrix} ^ n \begin{pmatrix} I \\ 0 \\ \end{pmatrix} $$
と書くことができるので、繰り返し二乗法を適用するとでが求められます。
後はにを掛ければ、第項までの和が求まります。
$$ S_{n+1} \begin{pmatrix} a_{m -1} \\ a_{m -2} \\ \vdots \\ a_{0} \\ \end{pmatrix} = ( I + A + \ldots + A ^ {i -1} + A ^ i ) \begin{pmatrix} a_{m -1} \\ a_{m -2} \\ \vdots \\ a_{0} \\ \end{pmatrix} $$
$$ = \begin{pmatrix} a_{m -1} \\ a_{m -2} \\ \vdots \\ a_{0} \\ \end{pmatrix} + \begin{pmatrix} a_{m} \\ a_{m -1} \\ \vdots \\ a_{1} \\ \end{pmatrix} + \begin{pmatrix} a_{n+m -1} \\ a_{n+m -2} \\ \vdots \\ a_{n} \\ \end{pmatrix} $$
$$ = \begin{pmatrix} a_{m -1} + a_{m -2} + \ldots + a_{n+m -1} \\ a_{m -2} + a_{m -3} + \ldots + a_{n+m -2} \\ \vdots \\ a_{0} + a_{1} + \ldots + a_{n} \\ \end{pmatrix} $$
このことは蟻本で行列の累乗和を求める方法として説明されています。
kitamasa法による項間漸化式の第項の計算
項間漸化式の第項はより高速に計算する方法があります。
日本の競技プログラミング界隈ではkitamasa法と呼ばれます。*3
ここでは詳しい説明はしません。
気になる人はm項間漸化式の高速なアルゴリズム - 競技プログラミングをするんだよや高速 Kitamasa 法 - みさわめも等のブログ記事を読んで下さい。
ここで重要な点は、kitamasa法を用いると項間漸化式の第項がで計算できるという点です。*4
本題
項間漸化式の第項までの和
項間漸化式の第項までの和は、行列の累乗和を陽に求めなくても計算できます。
アイデアは簡単で、第項までの総和を
として、項間漸化式から、総和の漸化式
を構成することを考えます。
結論だけ言うと、次のような関係になります。*5
つまり項間漸化式について、
この関係はとから導くことができるので、計算してみてください。
これにより、項間漸化式の第項までの和を求める問題を、項間漸化式の第項を求める問題に変換できました。
すでに説明したとおり、項間漸化式の第項はkitamasa法によりで求めることができるので、項間漸化式の第項までの和もで求められるようになりました。*6
実験
項間漸化式の第項までの和を求める計算を以下の3つの方法で行い、実行速度を比較しました。
- 前から順番に求めて足していく
- 行列の累乗和
- 総和の漸化式+kitamasa法
言語はC++です。答えは非常に大きくなりうるのででmodを取っています*7。その他の詳細な設定はリンク先で確認してください。
kitamasa法のソースコードはこちらからお借りしました。
累乗和の実装は蟻本を参考にしています。
結果は以下の表のようになりました。
$$m=3,n=10 ^ 6$$ | $$m=10,n=10 ^ 6$$ | $$m=100,n=10 ^ 8$$ | |
---|---|---|---|
前から足していく | 21.5772ms | 75.9941 ms | -(時間がかかりすぎるので省略) |
行列の累乗和 | 0.024ms | 0.6107 ms | 1764.7 ms |
総和の漸化式+kitamasa | 0.0153ms | 0.0991 ms | 12.0769 ms |
詳細 | https://wandbox.org/permlink/uVmogwgBVcEINwYf | https://wandbox.org/permlink/6HbnojdzEU61JMgP | https://wandbox.org/permlink/GYXVyPbczVkOEen4 |
粗い測定ですが、mが小さいときから大きいときまで、一貫して総和の漸化式+kitamasa法が高速であることが確認できました。
一応gistにもソースコードを上げておきます。ブログの説明とは細かい添字が違ったりするので注意してください。
https://gist.github.com/tosaka2/e1b4e9dcb7892568d16570075d85941a
余談
である場合、総和の漸化式への変換を使って項間漸化式の第項をで求めることができます。
これに限らず係数がある条件を満たす場合は、総和を経由することにより、第項をで求められます。*8
このアルゴリズムはのように、が大きく、とのスケールが近いとき、kitamasa法を使うよりも高速な解法になり得ます。
所感
コンテストでこれが必要になることは無いと思うので、まぁ小ネタです。
息抜きに書くつもりがはてなブログの数式の仕様と格闘していたら思いの外時間がかかってしまいました...
何か間違い等あればご指摘ください。
*1:この書き方だと項間漸化式な気もしますが、蟻本に合わせてこの式を項間漸化式と呼ぶことにします。
*2:プログラミングコンテストチャレンジブック 第2版 P.180~
*3:コンパニオン行列のべき乗
*4:高速kitamasa法を用いるとまで計算量が落ちます
*5:はてなで上手く数式がかけないのでこの書き方になりました...
*6:とを前処理で計算する必要がありますが、これはどちらもで計算できます
*7:特に総和の漸化式+kitamasa法では係数が負になるので、元の数列の係数が全て正でも負のmodの処理をしてあげる必要があります
*8:総和に変換→で次の項が求められる式に変形(これができることが条件)→からまで順に求める→で項目が求まる