快速傅里叶变换
FFT#
问题:
设
有一个结论:坐标系中
可以这样理解:
于是我们可以换一种思路求卷积:将
但是,这样子复杂度仍然不变,那该怎么办呢?
于是我们考虑用复数解决问题,在一个单位圆上,圆上的点表示
1.
2.
3.
4.
然后,考虑如何快速计算多项式的值,通过思考可以得到:
注意:
但是这并不能优化我们的复杂度,于是我认为 FFT 一个很厉害的地方来了,我们将单位根
当
当
这样,我们就有了较大的突破,因为这样子,我们要求的东西就是可转移的了,它就相当于一个
void FFT (int lim, comp F[])
{
if (lim == 1) return ;
comp F1[lim >> 1], F2[lim >> 1];
rep (i, 0, (lim >> 1) - 1) F1[i] = F[i << 1], F2[i] = F[i << 1 | 1];
FFT (lim >> 1, F1), FFT (lim >> 1, F2);
comp w = (comp) {1, 0};
comp wk = (comp) {cos (2.0 * PI / lim), sin (2.0 * PI / lim)};
for (int i = 0; i < (lim >> 1); ++ i, w = w * wk)
{
F[i] = F1[i] + F2[i] * w;
F[i + (lim >> 1)] = F1[i] - F2[i] * w;
}
}
我认为仔细看是可以看懂的,首先我们把系数带进自己的左右节点的数组,然后我们发现每一层完成以后自己的数组变成了每一个
于是每个需要的 (
我们设
此时,当
设
void FFT (int lim, comp F[], int op)
{
if (lim == 1) return ;
comp F1[lim >> 1], F2[lim >> 1];
rep (i, 0, (lim >> 1) - 1) F1[i] = F[i << 1], F2[i] = F[i << 1 | 1];
FFT (lim >> 1, F1, op), FFT (lim >> 1, F2, op);
comp w = (comp) {1, 0};
comp wk = (comp) {cos (2.0 * PI / lim), sin (2.0 * PI / lim) * op};
for (int i = 0; i < (lim >> 1); ++ i, w = w * wk)
{
F[i] = F1[i] + F2[i] * w;
F[i + (lim >> 1)] = F1[i] - F2[i] * w;
}
}
signed main ()
{
// freopen ("1.in", "r", stdin);
// freopen ("1.out", "w", stdout);
n = rd (), m = rd ();
int l = 0;
for (lim = 1; lim <= n + m; lim <<= 1, ++ l) ;
rep (i, 1, lim) R[i] = (R[i >> 1] >> 1) | (i & 1) << (l - 1);
rep (i, 0, n) A[i].x = rd ();
rep (i, 0, m) B[i].x = rd ();
FFT (lim, A, 1), FFT (lim, B, 1);
rep (i, 0, lim) A[i] = A[i] * B[i];
FFT (lim, A, -1);
rep (i, 0, n + m) printf ("%lld ", (int) (A[i].x / lim + 0.5));
}
这里的
这个写法效率很慢,有一个叫蝴蝶变换的更优秀的规律可以优化它,这里不多赘述。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?