简单来说就是一个计算多项式乘法的东西呀..

以下内容基本都是在大黑书《算法导论》上的..


总述

对于项数为$n$的多项式$A(x)$和项数为$m$的多项式$B(x)$,可以如此表达:

$$A(x)=A_0+A_1x+A_2x^2+A_3x^3+...+A_{n-1}x^{n-1}$$

$$B(x)=B_0+B_1x+B_2x^2+B_3x^3+...+B_{m-1}x^{m-1}$$

把这两个多项式相乘可得到项数为$(n+m-1)$的多项式$C(x)$:

其中对于其任意系数$C_i$有$C_i=\sum\limits_{j=0}^{i}A_j\cdot B_{i-j}$

然后的话具体步骤如下:

系数表达->点值表达->相乘->点值表达->系数表达

把系数表达式转为点值表达式以及其逆运算都是$O(nlogn)$的

相乘不用说,$O(n)$

总复杂度为$O(nlogn)$


点值表达式

简单来说就是把多项式$A(x)$代入若干个$x$的值得到若干个点

比如说$A(x)$的点值表达式为${(x_0,A(x_0)),(x_1,A(x_1)),(x_2,A(x_2)),...,(x_{n-1},A(x_{n-1}))}$

我们把从点值表达式转化为系数表达式的操作称为插值

插值多项式的唯一性定理

对于有$n$个点组成的集合仅存在一个项数为$n$的多项式与之对应

FFT即为取若干个特殊的值进行点值表达


n次单位复数根

从这里开始,所有提到的$n$将满足$(n=2^k,k\in N)$

n次单位复数根:即$\omega$使得$\omega^n=1$

复数与$e$的关系:$$e^{iu}=\cos u+i\sin u$$

(上述公式由于复数运算性质与指数运算性质大致相同,可以用泰勒公式证明,这里挖一个坑)

由于$e^{2\pi i}=\cos 2\pi+i\sin 2\pi=1$

所以定义主n次单位根为$\omega_n=e^{2\pi i/n}$

那么从上面可得 $\omega_n^i\ (i=0,1,2,...,n-1)$在平面直角坐标系上均匀分布且模长为$1$

也就有$\omega_n^i\cdot\omega_n^j=\omega_n^{(i+j)\ mod\ n}$


一些定理

消去引理: $$\omega_{dn}^{dk}=\omega_n^k$$

证明$$\omega_{dn}^{dk}=e^{2\pi i\cdot dk/dn}=e^{2\pi i\cdot k/n}=\omega_n^k$$

折半引理:如果$n>0$且$n$为偶数,那么$n$个n次单位复数根的平方的集合就是$n/2$个n/2次单位复数根的集合

证明

$$(\omega_n^k)^2=\omega_{n/2}^k$$

$$(\omega_n^{k+n/2})^2=\omega_n^{2k+n}=\omega_n^{2k}\cdot\omega_n^n=\omega_{n/2}^k$$

由此我们可以得到$$(\omega_n^k)^2=(\omega_n^{k+n/2})^2$$

即$$\omega_n^k=-\omega_n^{k+n/2}$$

这将会很有用

求和引理:对于$n\in Z,n\geq 1$,且$n\nmid k$,有$$\sum\limits_{j=0}^{n-1}(\omega_n^k)^j=0$$

由等比数列求和公式可得$$\sum\limits_{j=0}^{n-1}(\omega_n^k)^j=\dfrac{(\omega_n^k)^n-1}{\omega_n^k-1}=0$$


DFT

我们计算多项式$A(x)=\sum\limits_{i=0}^{n-1}a_ix^i$在$\omega_n^0,\omega_n^1,\omega_n^2,...,\omega_n^{n-1}$处的结果

也就是其点值表达式$(y_0,y_1,y_2,...,y_{n-1})$

称其为$(a_0,a_1,a_2,...,a_{n-1})$的离散傅里叶变换

$$y=DFT_n(a)$$


FFT

利用复数单位根的特殊性质即可快速求出(即在$O(nlogn)$时间内)求出$DFT_n(a)$

这是一种分治策略,先把偶数系数和奇数系数的分开

$$A^{[0]}(x)=a_0+a_2x+a_4x^2+...+a_{n-2}x^{n/2-1}$$

$$A^{[1]}(x)=a_1+a_3x+a_5x^2+...+a_{n-1}x^{n/2-1}$$

所以可以得到

$$A(x)=A^{[0]}(x^2)+xA^{[1]}(x^2)$$

假设通过递归分治前两者已经求好

对于$k\in [0,n/2-1]$

$$y_k=y^{[0]}_k+\omega_n^ky^{[1]}_k$$

$$y_{k+n/2}=y^{[0]}_k-\omega_n^ky^{[1]}_k$$

证明:

$y_k=y^{[0]}_k+\omega_n^ky^{[1]}_k$

$=A^{[0]}(\omega_{n/2}^k)+\omega_n^kA^{[1]}(\omega_{n/2}^k)$

$=A^{[0]}((\omega_n^k)^2)+\omega_n^kA^{[1]}((\omega_n^k)^2)$

$=A(\omega_n^k)$

同样的

$y_{k+n/2}=y^{[0]}_k-\omega_n^ky^{[1]}_k$

$=A^{[0]}(\omega_{n/2}^k)+\omega_n^{k+n/2}A^{[1]}(\omega_{n/2}^k)$

$=A^{[0]}((\omega_n^{k+n/2})^2)+\omega_n^{k+n/2}A^{[1]}((\omega_n^{k+n/2})^2)$

$=A(\omega_n^{k+n/2})$

证明结束,我们此时把$\omega_n^k$称作旋转因子


插值

由前面的式子,可以把$DFT$写成矩阵乘积的形式$y=V_na$

其中$V_n$是由$\omega_n$的若干次幂填充的矩阵

$$\left[\begin{array}{aaa}y_0 \\ y_1 \\ y_2 \\ y_3 \\ \vdots \\ y_{n-1}\end{array}\right]=\left[\begin{array}{ccc}1 & 1 & 1 & 1 \ldots & 1 \\1 & \omega_n & \omega_n^2 & \omega_n^3 & \ldots & \omega_n^{n-1} \\1 & \omega_n^2 & \omega_n^4 & \omega_n^6 & \ldots & \omega_n^{2(n-1)} \\1 & \omega_n^3 & \omega_n^6 & \omega_n^9 & \ldots & \omega_n^{3(n-1)} \\\vdots & \vdots & \vdots & \vdots & \ddots & \vdots \\1 & \omega_n^{n-1} & \omega_n^{2(n-1)} & \omega_n^{3(n-1)} & \ldots & \omega_n^{(n-1)(n-1)}\end{array}\right]\left[\begin{array}{ppp}a_0 \\ a_1 \\ a_2 \\ a_3 \\ \vdots \\ y_{n-1}\end{array}\right]$$

对于$j,k\in [0,n-1]$,$V_n$的$(j,k)$元素为$\omega_n^{kj}$

所以$$a=y\cdot V_n^{-1}$$

定理:对于$j,k\in [0,n-1]$,$V_n^{-1}$的$(j,k)$元素为$\omega_n^{-kj}/n$

证明:我们只需证明$V_n^{-1}V_n=I_n$,即$n\times n$的单位矩阵

对于该矩阵$(j,j')$的元素

$$\left[V_n^{-1}V_n\right]_jj'=\sum\limits_{k=0}^{n-1}(\omega_n^{-kj}/n)(\omega_n^{kj'})=\sum\limits_{k=0}^{n-1}\omega_n^{k(j'-j)}/n$$

那么如果$j=j'$此和为$1$,否则根据求和引理,此和为$0$

证明完成

由此可得$DFT_n^{-1}(y)$

$$a_j=\dfrac{1}{n}\sum\limits_{k=0}^{n-1}y_k\cdot \omega_n^{-kj}$$


高效实现FFT

也就是使用迭代代替递归

比如$n=8$,以如下排列顺序即可实现

$${a_0,a_4,a_2,a_6,a_1,a_5,a_3,a_7}$$

也称作位逆序置换


Code

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int Maxn = 400010;
struct cp {
double r, i;
cp (double r = 0.0, double i = 0.0) : r(r), i(i) {}
}A[Maxn], B[Maxn]; int An, Bn, N;
cp operator +(cp a, cp b) { return cp(a.r+b.r, a.i+b.i); }
cp operator -(cp a, cp b) { return cp(a.r-b.r, a.i-b.i); }
cp operator *(cp a, cp b) { return cp(a.r*b.r-a.i*b.i, a.r*b.i+a.i*b.r); }
const double PI = acos(-1);
void fft(cp *a, int op) {
int i, j, k;
j = 0;
for(i = 0; i < N; i++){
if(i < j) swap(a[i], a[j]);
k = N >> 1;
while(j & k){ j -= k; k >>= 1; }
j += k;
}
for(i = 2; i <= N; i <<= 1){
cp wn = cp(cos(2.0*PI/i), op*sin(2.0*PI/i));
for(j = 0; j < N; j += i){
cp w = cp(1, 0);
for(k = j; k < j+i/2; k++){
cp x = a[k], y = w*a[k+i/2];
a[k] = x+y; a[k+i/2] = x-y;
w = w*wn;
}
}
}
if(op == -1) for(i = 0; i < N; i++) A[i].r /= N;
}
int main() {
int i, j, k;
scanf("%d%d", &An, &Bn);
N = 1;
while(N-1 <= An+Bn) N <<= 1;
for(i = 0; i <= An; i++) scanf("%lf", &A[i].r);
for(i = 0; i <= Bn; i++) scanf("%lf", &B[i].r);
fft(A, 1); fft(B, 1);
for(i = 0; i < N; i++) A[i] = A[i]*B[i];
fft(A, -1);
for(i = 0; i <= An+Bn; i++) printf("%d ", (int)(A[i].r+0.5));
printf("\n");
return 0;
}

  


完结撒花..

拖了差不多半年的坑终于补上了..

FFT是个啥?的更多相关文章

  1. 并行计算提升32K*32K点(32位浮点数) FFT计算速度(4核八线程E3处理器)

    对32K*32K的随机数矩阵进行FFT变换,数的格式是32位浮点数.将产生的数据存放在堆上,对每一行数据进行N=32K的FFT,记录32K次fft的时间. 比较串行for循环和并行for循环的运行时间 ...

  2. 【BZOJ-2179&2194】FFT快速傅里叶&快速傅里叶之二 FFT

    2179: FFT快速傅立叶 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 2978  Solved: 1523[Submit][Status][Di ...

  3. 为什么FFT时域补0后,经FFT变换就是频域进行内插?

    应该这样来理解这个问题: 补0后的DFT(FFT是DFT的快速算法),实际上公式并没变,变化的只是频域项(如:补0前FFT计算得到的是m*2*pi/M处的频域值, 而补0后得到的是n*2*pi/N处的 ...

  4. FFT NNT

    算算劳资已经多久没学新算法了,又要重新开始学辣.直接扔板子,跑...话说FFT算法导论里讲的真不错,去看下就懂了. //FFT#include <cstdio> #include < ...

  5. CC countari & 分块+FFT

    题意: 求一个序列中顺序的长度为3的等差数列. SOL: 对于这种计数问题都是用个数的卷积来进行统计.然而对于这个题有顺序的限制,不好直接统计,于是竟然可以分块?惊为天人... 考虑分块以后的序列: ...

  6. ECF R9(632E) & FFT

    Description: 上一篇blog. Solution: 同样我们可以用fft来做...就像上次写的那道3-idoit一样,对a做k次卷积就好了. 同样有许多需要注意的地方:我们只是判断可行性, ...

  7. fft练习

    数学相关一直都好弱啊>_< 窝这个月要补一补数学啦, 先从基础的fft补起吧! 现在做了 道. 窝的fft 模板 (bzoj 2179) #include <iostream> ...

  8. FFT时域与频域的关系,以及采样速率与采样点的影响

    首先对于FFT来说,输入的信号是一个按一定采样频率获得的信号序列,而输出是每个采样点对应的频率的幅度(能量). 下面详细分析: 在FFT的输出数据中,第一个值是直流分量的振幅(这样对应周期有无穷的可能 ...

  9. 【玩转单片机系列002】 如何使用STM32提供的DSP库进行FFT

    前些日子,因为需要在STM32F103系列处理器上,对采集的音频信号进行FFT,所以花了一些时间来研究如何高效并精确的在STM32F103系列处理器上实现FFT.在网上找了很多这方面的资料做实验并进行 ...

  10. FFT

    void FFT(complex a[],int n,int fl){ ,j=n/;i<n;i++){ if (i<j) {complex t=a[i];a[i]=a[j];a[j]=t; ...

随机推荐

  1. 调试WebApi的一些方法

    1.Get方法时,直接用浏览器访问 2.Postman 3.用HttpClient调用 privatevoid GetData() { using (HttpClient client = new H ...

  2. last与lastb命令 读取的日志文件

    在linux系统中,last与lastb命令用来列出目前与过去登录系统的用户相关信息.指令英文原义: last, lastb - show listing of last logged in user ...

  3. 读C#开发实战1200例子记录-2017年8月14日10:03:55

    C# 语言基础应用,注释 "///"标记不仅仅可以为代码段添加说明,它还有一项更重要的工作,就是用于生成自动文档.自动文档一般用于描述项目,是项目更加清晰直观.在VisualStu ...

  4. SQL Server常见的操作符

    常见的操作符:Sort.Hash Match(聚合).Filter.Compute Scalar等 一:Sort select Shelf from Production.ProductInvento ...

  5. 开始写博客,学习Linq(1)

    摘自<linq实战>原文: 软件很简单.它可以归结为两件事情:代码和数据. 开发软件却并非那么简单,其中很重要的一项任务就是编写处理数据的代码. 无论选择了哪种语言,在程序开发得某个时候你 ...

  6. C# Enum,Int,String的互相转换 [转]

    C# Enum,Int,String的互相转换 Enum为枚举提供基类,其基础类型可以是除 Char 外的任何整型.如果没有显式声明基础类型,则使用 Int32.编程语言通常提供语法来声明由一组已命名 ...

  7. 【UOJ244】【UER #7】短路

    题解: 感觉贪心水平有所提高.. 首先比较显然的事情是我们可以枚举最深进行到哪一层 我们会发现,当且仅当该层是最小值才会使用决策, 并且是从该层的左上,走到右下 另外中间步骤就是(好难描述啊)一个单调 ...

  8. Introduction to boundary integral equations in BEM

    Boundary element method (BEM) is an effective tool compared to finite element method (FEM) for resol ...

  9. 一起学Hive——总结各种Join连接的用法

    Hive支持常用的SQL join语句,例如内连接.左外连接.右外连接以及HiVe独有的map端连接.其中map端连接是用于优化Hive连接查询的一个重要技巧. 在介绍各种连接之前,先准备好表和数据. ...

  10. Codeforces 830C Bamboo Partition 其他

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF830C.html 题解 把问题转化成求最大的 $d$ ,满足$$\sum_{1\leq i \leq n}( ...