02.md
は、 noshi91 さんによる発表 に始まるアルゴリズム群に対する、 Library Checker 提出 #199636 に至るまでの効率化のカジュアルなメモです。(当初は ScrapBox で公開、最終更新 : 2024-03-29)
ぜひ maspy さんによってもっと整理された投稿「 FPS 合成・逆関数の解説 (1) 逆関数と Power Projection」を参考にしてください。
02.md
は、 noshi91 さんによる発表 に始まるアルゴリズム群に対する、 Library Checker 提出 #199636 に至るまでの効率化のカジュアルなメモです。(当初は ScrapBox で公開、最終更新 : 2024-03-29)
ぜひ maspy さんによってもっと整理された投稿「 FPS 合成・逆関数の解説 (1) 逆関数と Power Projection」を参考にしてください。
noshi91 さんがこの間発表した↓
https://noshi91.hatenablog.com/entry/2024/03/16/224034
のアルゴリズムを頑張って実装してみたが、意味不明ソースコードになってしまって読むのに適さない。写経する、とかの方法もあるが、きっと各々のライブラリの都合でうまく行かないだろう。
inverse https://judge.yosupo.jp/submission/197749
composition https://judge.yosupo.jp/submission/197722
まあ、( multipoint eval とか Bostan--Mori のように)じきに定数倍まで研究されて発表されるんだろうけども、今実装したい人もいるでしょう、ということで
https://noshi91.hatenablog.com/entry/2023/12/10/163348
AtCoder Library 等の FFT の実装(よくある実装 ・「バタフライ演算」)では、 FFT した後の列が分かっているときにもとの列(を多項式としてみたとき)の偶数次の係数を取り出すのが非常に簡単。線形漸化式のための Bostan--Mori 法の高速化で重要だった方法である。一方で奇数次の係数を取り出したいときは、 FFT するときにずらしてやればいい。
上位桁の切り捨てがあるので線形漸化式のときほどうまくはいかないが、少なくとも、偶数次を先に取り出せば inverse FFT の長さが半分で済む。
ここで 2 次元 FFT の使用を仮定する。
int fg = (d <= e ? 1 : 0); // x 軸と y 軸のどちらが長いか?
int m = n/2;
for(int t=0; t<2; t++){
if(fg^t){ // 長いほうをさきに処理する : 先にやるほうは長さが半分でよい
ibat(q, d, e, 1);
if(d == 1) break;
for(int i=0; i<d; i++) q1[i] = q[i];
for(int i=0; i<m; i++) q1[d+i] = Zero;
for(int i=d; i<m; i++) q1[m+i] = q[i];
bat(q1, d, e*2, 1);
e *= 2; m *= 2; // 各軸の長さを更新
} else {
for(int i=0; i<m*2; i+=d*2){
for(int j=0; j<d; j++) q1[i+j] = q[i/2+j];
for(int j=0; j<d; j++) q1[i+d+j] = Zero;
}
bat(q1, 1, d*2, e);
d *= 2; m *= 2; // 各軸の長さを更新
}
std::swap(q, q1); std::swap(q1, q2); // バッファを切り替える
}
FFT の転置は、「先頭以外を reverse して FFT 」である。(よくある実装の) FFT をした後でも、区間
要素の並べ替えは普通に行列を書いて転置すればイメージしやすそう。
分母は、計算される順番と使う順番が逆だから、分母に FFT (の転置)を実行したものを全部スタックに積んでおく。(分母の FFT を転置しておけば、普通に畳みこんだときに middle product が計算できる。)