$\newcommand{\O}{\mathrm{O}}$
高速フーリエ変換のアルゴリズム. 畳み込みの演算を $\O (n \log n)$ で行うアルゴリズム(Cooley-Tukey).
信号処理をはじめ色々なところで用いられるアルゴリズム.
関数を $1$ の原始 $n$ 乗根で表現し, 分割統治の要領で再帰的に計算を行う(バタフライ演算とか呼ばれる).
実装では complex 型の乗算がデフォルトでは少々遅いためオーバーロードしている.
FFT は他の人のでたぶんもっと高速に動作するものがあり, そういう意味でこれはそんなに速くない.
(関数)
mul$(a, b)$ : 配列 $a, b$ の畳み込みを行う
時間計算量: $\O (n \log n)$
complex<double> operator*(const complex<double> a, const complex<double> b) { return complex<double>(a.real()*b.real()-a.imag()*b.imag(),a.real()*b.imag()+a.imag()*b.real()); } void fft(vector<complex<double> >& a,bool rev = false) { const double PI = 3.14159265358979324; unsigned int i, j, k, l; double r; complex<double> p, q, s; const unsigned int size = a.size(); if(size == 1) return; vector<complex<double> > b(size); r = rev ? (- 2 * PI / size) : 2 * PI / size; vector<complex<double> > kp(size / 2 + 1, 1); for(i = 0; i <= size / 2; ++i) kp[i] = polar((double)1.0, i * r); for(i = 1, l = size / 2; i < size; i <<= 1, l >>= 1){ for(j = 0, r = 0; j < l; ++j, r += i){ for(k = 0, s = kp[i * j]; k < i; ++k){ p = a[k + r], q = a[k + r + size / 2]; b[k + 2 * r] = p + q, b[k + 2 * r + i] = (p - q) * s; } } swap(a, b); } if(rev){ for(i = 0; i < size; i++){ a[i] /= size; } } } vector<int> mul(const vector<int>& a, const vector<int>& b) { int s = (int)a.size() + (int)b.size() - 1, t = 1; while(t < s) t *= 2; vector<complex<double> > A(t), B(t); for(int i = 0; i < (int)a.size(); i++) A[i].real(a[i]); for(int i = 0; i < (int)b.size(); i++) B[i].real(b[i]); fft(A), fft(B); for(int i = 0; i < t; i++) A[i] *= B[i]; fft(A, true); vector<int> res(s); for(int i = 0; i < s; i++) res[i] = round(A[i].real()); return res; }