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

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


总述

对于项数为$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. jdbc问题:Access denied for user ''@'localhost''是因为没输入账户和密码

    Access denied for user ''@'localhost' to database 'bjpowernode'

  2. HDU 1247 Hat’s Words(字典树活用)

    Hat's Words Time Limit : 2000 / 1000 MS(Java / Others)    Memory Limit : 65536 / 32768 K(Java / Othe ...

  3. 011-Python-进程、线程于协程

    1.进程与线程 进程: 一个程序要运行时所需的所有资源的集合: 一个进程至少需要一个线程,这个线程称为主线程,一个进程里可以包含多个线程: cpu 核数越多,代表着你可以真正并发的线程越多2个进程之间 ...

  4. centos7 yum install redis

    直接yum 安装的redis 不是最新版本 yum install redis 如果要安装最新的redis,需要安装Remi的软件源,官网地址:http://rpms.famillecollet.co ...

  5. 【Android】Android 代码判断当前设备是否为模拟器

    [Android]Android 代码判断当前设备是否为模拟器 方法比较简单,直接粘贴代码 //判断当前设备是否是模拟器.如果返回TRUE,则当前是模拟器,不是返回FALSE public stati ...

  6. Centos7防火墙常用命令及mask锁定不能添加端口问题

    一.开放端口 sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent sudo firewall-cmd --reload 二. ...

  7. 关于java中的伪共享的认识和解决

    在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素: CPU缓存 网页浏览器为了加快速度,会在本机存缓存以前浏览过 ...

  8. P1025 数的划分 dfs dp

    题目描述 将整数nn分成kk份,且每份不能为空,任意两个方案不相同(不考虑顺序). 例如:n=7n=7,k=3k=3,下面三种分法被认为是相同的. 1,1,51,1,5;1,5,11,5,1;5,1, ...

  9. 01. Numpy模块

    1.科学计算工具-Numpy基础数据结构 1.1.数组ndarray的属性 NumPy数组是一个多维数组对象,称为ndarray.其由两部分组成:① 实际的数据② 描述这些数据的元数据 注意数组格式, ...

  10. Extracted SQL state class 'S1' from value 'S1009'

    发现不查所有字段时是可以查询的,最后一个个字段尝试,发现是在passwd_time这个字段时有问题,然后看看这个时间 是无效的时间,改成有效时间即可.     相关链接: Mysql 时间 '0000 ...