FFT学习笔记
FFT
FFT 常用于加速多项式乘法。
点值表示法#
先考虑如何表示一个多项式。
最常见的是给定长度为
另一种方法叫做点值表示法,也就是用平面上
假设有两个多项式
而 FFT 的作用就是:将系数表示法在
快速傅里叶变换#
对于一个多项式
先考虑一种特殊情况:
幸运的是
对于任意一个
令
显然
而
这里有个问题:
显然实数是绝对不行的,考虑在复数域上取
单位根#
令
每个复数
复数
在复平面上作单位圆,然后将单位圆
因为
回到 FFT,我们找到了平方之后也能正负成对的
然后
快速傅里叶逆变换#
已经完成系数表示法到点值表示法的转换了,现在要转回去。
设系数为
而从
直接矩阵乘向量是
Code
#include <bits/stdc++.h>
const int M = 5e6 + 10;
const double pi = acos(-1);
struct Complex{
double real, imag;
Complex(double _r = 0, double _i = 0){
real = _r, imag = _i;
}
};
inline Complex operator + (Complex A, Complex B){
return Complex(A.real + B.real, A.imag + B.imag);
}
inline Complex operator - (Complex A, Complex B){
return Complex(A.real - B.real, A.imag - B.imag);
}
inline Complex operator * (Complex A, Complex B){
return Complex(A.real * B.real - A.imag * B.imag, A.real * B.imag + B.real * A.imag);
}
int n, m, len;
Complex a[M], b[M];
inline void FFT(Complex* a, int len, int v) {
if (len == 1) return;
Complex e[len >> 1], o[len >> 1];
for (int i = 0; i < len; i += 2)
e[i >> 1] = a[i], o[i >> 1] = a[i + 1];
FFT(o, len >> 1, v), FFT(e, len >> 1, v);
Complex Wn(cos(2 * pi / len), v * sin(2 * pi / len)), w(1, 0);
for (int i = 0; i < (len >> 1); i++, w = w * Wn)
a[i] = e[i] + w * o[i], a[i + (len >> 1)] = e[i] - w * o[i];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; i++) scanf("%lf", &a[i].real);
for (int i = 0; i <= m; i++) scanf("%lf", &b[i].real);
len = 1;
while (len < n + m + 1) len <<= 1;
FFT(a, len, 1), FFT(b, len, 1);
for (int i = 0; i <= len; i++) a[i] = a[i] * b[i];
FFT(a, len, -1);
for (int i = 0; i <= n + m; i++)
printf("%d ", (int)(a[i].real / len + 0.5));
return 0;
}
蝴蝶变换#
递归的开销太大了,考虑迭代实现 FFT。
假设一个八阶 FFT,在递归的过程中,考虑每个元素的位置:
第一层:
第二层:
第三层:
第四层:
数字 | 二进制 |
---|---|
容易发现,如果将二进制表示倒过来,就是
Code
inline void FFT(Complex* a, int v) {
for (int i = 0; i < n; i++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (n >> 1) : 0);
for (int i = 0; i < n; i++)
if (rev[i] > i) std::swap(a[i], a[rev[i]]);
for (int k = 2, l = 1; k <= n; k <<= 1, l <<= 1) {
Complex Wn = Complex(cos(2 * pii / k), v * sin(2 * pii / k));
for (int i = 0; i < n; i += k) {
Complex w = Complex(1, 0);
for (int j = 0; j < l; j++, w = w * Wn) {
Complex tmp = w * a[i + j + l];
a[i + j + l] = a[i + j] - tmp;
a[i + j] = a[i + j] + tmp;
}
}
}
}
作者:zzxLLL
出处:https://www.cnblogs.com/zzxLLL/p/17893319.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!