浅谈FFT

Fast Fourier Transportation(FFT)

·多项式的表达

系数表达

对于一个次数界为n的多项式\(A(x)=\sum_{j=0}^{n-1}{a_jx^j}?\)而言,其系数表达是由一个系数组成的向量\(a=(a_0,a_1,...,a_{n-1})?\)。

点值表达

一个次数界为n的多项式A(x)的点值表达就是一个由n个点值对所组成的集合
\[
{(x_0,y_0),(x_1,y_1),...,(x_{n-1},y_{n-1})}
\]
使得对k=0,1,...,n-1,所有\(x_k?\)各不相同,
\[
y_k=A(x_k)
\]
简单的求点值运算我们可以随意代入n个不相同的数,然后得出点对,时间复杂度\(\Theta(n^2)?\)。后面可以看到,如果我们用一点巧妙的取值,可以使时间复杂度优化到\(\Theta(n\lg_2n)?\)。

求值计算的逆(从一个多项式的点值表达确定的系数表达形式)称为插值

·多项式运算

\[
C_j=\sum_{k=0}^{j}{A_kB_{j-k}}
\]

上述是多项式的乘法,我们把C称为A和B的卷积(convolution),表示成\(C=A\bigotimes B\)。

FFT的主要思路是首先把A和B转成点值表达,然后得到C的点值表达,再逆着做一遍,得到C的系数表达。

·DFT与FFT

上述做法太慢,我们要用\(\Theta(n^2)\)的时间把每个多项式转成点值表达,然后再用\(\Theta(n^2)\)的时间转回来,明显很慢,还不如暴力。

我们想,可不可以使用某些特殊的数,使得每次可以做一次运算就可以得到多个数的呢?答案是有的:单位根复数根

n次单位复数根是满足\(\omega^n=1\)的复数\(\omega\)。n次单位复数根恰好有n个,对于k=0,1,...,n-1,这些根是\(e^{\pi ik/n}\)。为了解释这个表达式,我们用复数的指数形式来定义:
\[
e^{iu}=cos(u)+isin(u)
\]

也就是给定一个单位圆,上面均匀地分布着n个向量,如图:

·关于n次单位复根

以上图为例我们可以发现,每一个n(这里是8)次单位复根都是一个向量,他们在乘法意义下形成一个群。

引理1:(消去引理)
\[
对于任意整数n\geqslant0,k\geqslant0,以及d>0,
\]

\[
\omega^{dk}_{dk}=\omega^{k}_{n}
\]

证明
\[
\omega^{dk}_{dk}=(e^{2\pi i/dn})^{dk}=(e^{2\pi i/n})^k=\omega^{k}_{n}
\]
引理2:(折半引理)
\[
如果n>0为偶数,那么n个n次单位根的平方集合就是n/2个n/2次单位根的集合
\]
证明
\[
(\omega^{k+n/2}_{n})^2=\omega^{2k+n}_n=\omega^{2k}_n\omega^n_n=(\omega^k_n)^2
\]
因此\(\omega^k_n\)与\(\omega^{k+n/2}_n\)平方相等。

引理3:(求和引理)
\[
对于任意整数n\geqslant0和不能被n整除的非负整数k,有
\]

\[
\sum_{j=0}^{n-1}(\omega^k_n)^j=0
\]

证明
\[
\sum_{j=0}^{n-1}(\omega^k_n)^j=\frac{(\omega^k_n)^0(1-(\omega^k_n)^n)}{1-\omega^{k}_{n}}=\frac{(\omega^n_n)^k-1}{\omega^{k}_{n}-1}=\frac{(1)^k-1}{\omega^{k}_{n}-1}=0
\]
因为要求k不能被n整除,而且仅当k被n整除时\[\omega^k_n=1\]成立,同时保证分母不为0。

DFT

回顾一下,我们希望计算次数界为n的多项式
\[
A(x)=\sum_{j=0}^{n-1}{a_jx^j}
\]
在\[\omega^0_n,\omega^1_n,...,\omega^{n-1}_n\]处的值。假设A以系数形式给出,接下来定义结果\(y_k\):
\[
y_k=A(\omega^k_n)=\sum_{j=0}^{n-1}a_j\omega^{kj}_n
\]
向量\(y=(y_0,y_1,...,y_{n-1})\)就是系数向量\(a=(a_0,a_1,...,a_{n-1})\)的离散傅里叶变换(DFT)。我们也记作\(y=DFT_n(a)\)。

FFT

通过使用一种称为快速傅里叶变换(FFT)的方法,利用复根的特殊性质,我们就可以在\[\Theta(n\lg n)\]的时间内计算出\(DFT_n(a)\)。

注意:通篇的n我们假设是2的整数次幂。

FFT利用分治策略,采用A(x)中偶数下标的系数与奇数下标的系数,分别定义两个新的次数界为n/2的多项式

\[
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)
\]
所以原问题转化为求两个次数界为n/2的多项式\[A^{[0]}(x)\]和\[A^{[1]}(x)\]在点\[(\omega^0_n)^2,(\omega^1_n)^2,...,(\omega^{n-1}_n)^2\]的取值。

所以我们可以发现在求出\[A^{[0]}(x^2)\]和\[A^{[1]}(x^2)\]以后,可以算出两个复根的结果:
\[
y_k=y^{[0]}_k+\omega^k_ny^{[1]}_k
=A^{[0]}(\omega^{2k}_n)+\omega^k_nA^{[1]}(\omega^{2k}_n)
=A(\omega^k_n)
\]
还有
\[
y_{k+(n/2)}=y^{[0]}_k-\omega^{k}_ny^{[1]}_k
=y^{[0]}_k+\omega^{k+(n/2)}_ny^{[1]}_k
=A^{[0]}(\omega^{2k}_n)+\omega^{k+(n/2)}A^{[1]}(\omega^{2k}_n)
\]
\[
=A^{[0]}(\omega^{2k+n}_n)+\omega^{k+(n/2)}A^{[1]}(\omega^{2k+n}_n)
=A(\omega^{k+(n/2)}_n)
\]

所以就有代码:

void FFT(comp *a,int n,int inv){
    if(n==1) return;
    int mid=n/2;
    for (int i=0;i<mid;++i) c[i]=a[i*2],c[i+mid]=a[i*2+1];
    for (int i=0;i<n;++i) a[i]=c[i];
    FFT(a,mid,inv);
    FFT(a+mid,mid,inv);
    comp wn={cos(2.0*pi/n),inv*sin(2.0*pi/n)},w={1,0};
    for (int i=0;i<mid;++i,w=w*wn){
        c[i]=a[i]+w*a[i+mid];
        c[i+mid]=a[i]-w*a[i+mid];
    }
    for (int i=0;i<n;++i) a[i]=c[i];
}

·在单位复数根的插值

现在我们展示如何在单位复数根处插值来完成多项式乘法方案,使得我们把一个多项式从点值表达转换回系数表达。
我们可以把DFT写成矩阵乘积
\[
\left[
\begin{matrix}
y_0\ y_1\ y_2\ \vdots\ y_{n-1}
\end{matrix}
\right]=
\left[
\begin{matrix}
1 & 1 & 1 & 1 &\cdots& 1\ 1 & \omega_n & \omega^2_n & \omega^3_n &\cdots& \omega^{n-1}_n\ 1 & \omega^2_n & \omega^4_n & \omega^6_n &\cdots& \omega^{2(n-1)}_n\ 1 & \omega^3_n & \omega^6_n & \omega^9_n &\cdots& \omega^{3(n-1)}_n\ \vdots & \vdots& \vdots& \vdots& \ddots& \vdots\ 1 & \omega^{n-1}_n & \omega^{2(n-1)}_n & \omega^{3(n-1)}_n &\cdots& \omega^{(n-1)(n-1)}_n
\end{matrix}
\right]
\left[
\begin{matrix}
a_0\ a_1\ a_2\ \vdots\ a_{n-1}
\end{matrix}
\right]
\]
尴尬的是跑得贼慢:

随便卡卡就爆了....
分治难免递归,递归常数大。
于是,考虑改进。

·蝴蝶变换


盗图一张
可以发现,每个下标的二进制形式反过来就是它们最后在序列中的位置。
于是就有了迭代打法。

void FFT(Moon *a,int inv){
    int i,j,len;
    for (i=0;i<n;++i)
        if(i<R[i])swap(a[i],a[R[i]]);
    for (len=2;len<=n;len<<=1){
        int half=len/2;
        Moon w,wn={cos(Pi/half),inv*sin(Pi/half)};
        for (j=0;j<n-i;j+=len,w={1,0}){
            for (i=0;i<half;++i,w=w*wn){
                Moon q=w*a[j+half+i],Q=a[j+i];
                a[j+half+i]=Q-q;
                a[j+i]=Q+q;
            }
        }
    }
}
int main(){
    for (i=0;i<n;++i) R[i]=(R[i>>1]>>1)|((i&1)<<(p-1));
} 

原文地址:https://www.cnblogs.com/Chandery/p/11332777.html

时间: 2024-11-02 05:01:58

浅谈FFT的相关文章

多项式艺术:浅谈FFT和NTT算法(未完待续)

什么是多项式? 百度百科说:“由若干个单项式相加组成的代数式叫做多项式.多项式中每个单项式叫做多项式的项,这些单项式中的最高次数,就是这个多项式的次数.” 也就是说,形如的式子,就叫做多项式.这样的式子,也能写作.很显然,多项式加上(或是减上)多项式也是多项式,复杂度是的.但是,如果多项式想要乘上一个多项式,那么也可以,最简单的方法却是的. 不过,FFT算法会告诉你,就够了. 多项式乘法 我们说的,多项式想要乘上一个多项式,那就是多项式乘法,人称“卷积”.我们方才所看到的,被称为多项式的“系数表

浅谈FFT(快速傅里叶变换)

本文主要简单写写自己学习FFT的经历以及一些自己的理解和想法. FFT的介绍以及入门就不赘述了,网上有许多相关的资料,入门的话推荐这篇博客:FFT(最详细最通俗的入门手册),里面介绍得很详细. 为什么要学习FFT呢?因为FFT能将多项式乘法的时间复杂度由朴素的$O(n^2)$降到$O(nlogn)$,这相当于能将任意形如$f[k]=\sum\limits _{i+j=k}f[i]*f[j]$的转移方程的计算在$O(nlogn)$的时间内完成.因此对于想要进阶dp的同学来说,FFT是必须掌握的技能

浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理

浅谈范德蒙德(Vandermonde)方阵的逆矩阵与拉格朗日(Lagrange)插值的关系以及快速傅里叶变换(FFT)中IDFT的原理 只要稍微看过一点线性代数的应该都知道范德蒙德行列式. \[V(x_0,x_1,\cdots ,x_{n-1})=\begin{bmatrix} {1}&{1}&{\cdots}&{1}\{x_{0}}&{x_{1}}&{\cdots}&{x_{n-1}}\{x_{0}^2}&{x_{1}^2}&{\cdots

[转]浅谈ACM ICPC的题目风格和近几年题目的发展

斯坦福大学 王颖 ACM ICPC的比赛形式一般是五个小时八个题目,综合考察选手的数学能力.算法能力.coding能力和debug能力,还有团队配合能力.数学方面主要强调组合数学.图论和数论这三个方面的能力:而算法的覆盖范围很广,涉及了大部分经典的算法,和少量较前沿的算法.由于每道题目都需要通过所有的测试数据才能得分,并且需要精确解,这限制了Approximation algorithm在一些NP-hard的题目中的运用,从而使得搜索和剪枝策略对于NP-hard的题目非常重要. Final的题目

.net中对象序列化技术浅谈

.net中对象序列化技术浅谈 2009-03-11 阅读2756评论2 序列化是将对象状态转换为可保持或传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储和传输数 据.例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象.反之,反序列化根据流重新构造对象.此外还可以将对象序列化后保存到本地,再次运行的时候可以从本地文件 中“恢复”对象到序列化之前的状态.在.net中有提供了几种序列化的方式:二进制序列化

浅谈——页面静态化

现在互联网发展越来越迅速,对网站的性能要求越来越高,也就是如何应对高并发量.像12306需要应付上亿人同时来抢票,淘宝双十一--所以,如何提高网站的性能,是做网站都需要考虑的. 首先网站性能优化的方面有很多:1,使用缓存,最传统的一级二级缓存:2,将服务和数据库分开,使用不同的服务器,分工更加明确,效率更加高:3,分布式,提供多台服务器,利用反向代理服务器nginx进行反向代理,将请求分散开来:4,数据库的读写分离,不同的数据库,将读操作和写操作分开,并实时同步即可:5,分布式缓存,使用memc

单页应用SEO浅谈

单页应用SEO浅谈 前言 单页应用(Single Page Application)越来越受web开发者欢迎,单页应用的体验可以模拟原生应用,一次开发,多端兼容.单页应用并不是一个全新发明的技术,而是随着互联网的发展,满足用户体验的一种综合技术. SEO 一直以来,搜索引擎优化(SEO)是开发者容易忽略的部分.SEO是针对搜索(Google.百度.雅虎搜索等)在技术细节上的优化,例如语义.搜索关键词与内容相关性.收录量.搜索排名等.SEO也是同行.市场竞争常用的的营销手段.Google.百度的搜

浅谈html标签

浅谈html各常用标签用法 标题标签:<h1>-<h6>来表示,使标题字体变粗. <br />换行标记 <hr />水平分隔符 &nbsp空格符 &copy版权符 <a href>a标签超链接 href可接链接地址 <p>段落标签<blockquote>引用标签及可用做缩进 <table>表格中的<ul>无序列表<ol>有序列表<dl>自定义列表<row

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制