聊聊 fft 的单位根
与 ntt 不同,计算 fft 的单位根更复杂,要考虑精度问题.
关于fft精度的一点事实
fft精度比暴力dft高得多.原因主要是fft可以算作一种成对求和.
对fft精度的严格分析往往比经验精度要低很多.
原因有很多,比如实践中误差很难都同向舍入.
三次变两次(尤其在值域差距大时)精度误差较大.(三次变一点五次基本无此问题.)
可以认为直接从三角函数计算出的单位根精度是最高的.
由于 \(\sin(x)\) 和 \(\cos(x)\) 精度几乎无可指摘(仅仅不保证尾数的最后一位).
但传入幅角的精度有限制,因此 long double 计算再转 double 有意义(如果 long double 精度好于 double).
全部使用三角函数计算比较昂贵.(相对复数乘法而言,三角函数是相当慢的,尽管用三角恒等式精细实现只需要算 \(\dfrac{n}{4}\) 次.)
ps : 常见的 fft 写法对单位根做了 \(n\) 次乘法,可谓精度飞天.
可以考虑只计算 \(\log n\) 个单位根然后用乘积组合出剩下的单位根.
比如可以计算 \(2^k\) 次本原单位根然后二进制组合.
这样只有 \(\log n\) 次乘法(对于使用的单位根来说),是一种不错的办法.
这个思想是可以在线计算单位根而无需预处理出 \(O(n)\) 量级的表的.
另一个办法是用光速幂的思路.
计算 \(\sqrt n\) 个单位根然后用乘积组合出剩下所需的单位根.
这样只有 \(1\) 次乘法,可维持相当高的精度.
用这个思想在线计算是很简单的,直接做即可.
常规来说,在线计算意义并不大(往往变慢),但有些时候在线计算单位根能更好的利用 cache 从而减少常数.(尤其是变换长度极大时)
最后放一下一个对精度要求较高的题目,可以自行测试.
https://judge.yosupo.jp/problem/convolution_mod_1000000007
使用根号预处理(或者完全预处理)是可以用double跑过的,其他方式都比较困难.
参见 https://judge.yosupo.jp/submission/191165
这些提交都是 dif/dit 并利用 \(conj(w)=\dfrac{1}{w}\) 以不计算逆单位根或翻转.