IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 快速傅里叶变换FFT简明教程 -> 正文阅读

[数据结构与算法]快速傅里叶变换FFT简明教程

问题

给出长度为 n n n的多项式 f ( x ) = ∑ i = 0 n ? 1 a i x i f(x)=\sum_{i=0}^{n-1}a_{i}x^{i} f(x)=i=0n?1?ai?xi和长度为 m m m多项式 g ( x ) = ∑ i = 0 m ? 1 b i x i g(x)=\sum_{i=0}^{m-1}b_{i}x^{i} g(x)=i=0m?1?bi?xi。求解多项式 h ( x ) = f ( x ) × g ( x ) h(x)=f(x)\times g(x) h(x)=f(x)×g(x)
( 1 ≤ n ≤ 1 0 5 ) (1\leq n\leq 10^{5}) (1n105)

系数表示法和点值表示法

多项式的两种表示方法,系数表示法和点值表示法。
系数表示法:我们存储 f ( x ) f(x) f(x)的每一项 x i x^{i} xi的系数 a i a_{i} ai?
点值表示法:由大家都知道知, N N N个点可以确定一个最高次为 N ? 1 N-1 N?1多项式,所以我们可以存储多项式上任意 N N N个点,来确定这个多项式。

点值表示法有个性质是,对于多项式 f f f的一个点值 ( x , y 1 ) (x,y_{1}) (x,y1?),和多项式 g g g的一个点值 ( x , y 2 ) (x,y_{2}) (x,y2?),可以得到两个多项式相乘的多项式 h h h在横坐标为 x x x处的点为 ( x , y 1 × y 2 ) (x,y_{1}\times y_{2}) (x,y1?×y2?)

所以在对于长度为 n n n的多项式 f f f和长度为 m m m的多项式 g g g做乘法时,我们可以算出 f f f n + m ? 1 n+m-1 n+m?1个点值和 g g g n + m ? 1 n+m-1 n+m?1个点值,且要求两个多项式点值所取的 x x x相同。
我们就可以通过遍历算出这 n + m ? 1 n+m-1 n+m?1个横坐标的点值纵坐标相乘,来得到相乘后多项式的 n + m ? 1 n+m-1 n+m?1个点值,也就是新多项式的点值表示法。
(注意:虽然我们存储 f f f只需要 n n n个点值,但是为了计算出长度为 n + m ? 1 n+m-1 n+m?1 h h h,我们仍要先得到 f f f的(n+m-1)个点值)

因此,假如我们可以通过 o ( n l o g n ) o(nlogn) o(nlogn)的办法,将系数表示法转化为点值表示法,然后将两个多项式点值 o ( n ) o(n) o(n)相乘,最后通过 o ( n l o g n ) o(nlogn) o(nlogn)的办法将点值表示法转化回系数表示法,就可以解决这个问题了。

后面来介绍如何将系数表示发和点值表示法进行转换。

离散傅里叶变换(DFT)

对于没有周期的函数,我们可以认为其周期为 ∞ \infty ,用无穷个周期函数去拟合。
同理,对于一个长度为 N N N的离散多项式 x ( n ) ( n = 0 , 1 , 2.. N ? 1 ) x(n) (n=0,1,2..N-1) x(n)(n=0,1,2..N?1),可以通过 N N N个周期函数来拟合。( N N N个周期函数只能拟合 N N N个点,不能用来拟合连续多项式函数)
由此我们得到离散傅里叶级数展开式如下。
X ( k ) = ∑ n = 0 N ? 1 x ( n ) e ? 2 π i k n N X(k)=\sum_{n=0}^{N-1}x(n)e^{-2\pi ik\frac{n}{N}} X(k)=n=0N?1?x(n)e?2πikNn?
其中 e ? 2 π i k n N = c o s ( ? 2 π k n N ) + i s i n ( ? 2 π k n N ) e^{-2\pi ik\frac{n}{N}}=cos(-2\pi k\frac{n}{N})+isin(-2\pi k\frac{n}{N}) e?2πikNn?=cos(?2πkNn?)+isin(?2πkNn?)是关于 n n n的周期为 N N N的函数。(由欧拉公式 e i x = c o s x + i s i n x e^{ix}=cos x+isin x eix=cosx+isinx得)

离散傅里叶逆变换(IDFT)

对于变换后得到的 X ( n ) X(n) X(n),我们可以通过离散傅里叶逆变换公式得到原函数 x ( n ) x(n) x(n)。与 D F T DFT DFT公式差不多,在原来的基础上 i i i变成了 ? i -i ?i,前面乘上一个 1 N \frac{1}{N} N1?。公式如下。
x ( n ) = 1 N ∑ k = 0 N ? 1 X ( k ) e 2 π i k n N x(n)=\frac{1}{N}\sum_{k=0}^{N-1}X(k)e^{2\pi ik\frac{n}{N}} x(n)=N1?k=0N?1?X(k)e2πikNn?

快速傅里叶变换(FFT)

由前面讲过的,我们要解决的核心问题就是,算出多项式的 N N N个点值。
大致做法是,我们求出函数在离散傅里叶变换后的N个点值,由点值乘出新多项式后,再将其逆变换回系数表示法。

我们先来观察式子 D F T DFT DFT的式子。
我们为了简化式子,我们把 e 2 π i k N e^{\frac{2\pi ik}{N}} eN2πik? W N W_{N} WN?代替。( W N W_{N} WN?是一个关于 k k k的函数)
得到式子如下。
X ( k ) = ∑ n = 0 N ? 1 x ( n ) W N n ( k ) X(k)=\sum_{n=0}^{N-1}x(n)W_{N}^{n}(k) X(k)=n=0N?1?x(n)WNn?(k)
然后观察 W N W_{N} WN?如下。
W N ( k ) = e 2 π i N = c o s ( 2 π k N ) + i s i n ( 2 π k N ) W_{N}(k)=e^{\frac{2\pi i}{N}}=cos(\frac{2\pi k}{N})+isin(\frac{2\pi k}{N}) WN?(k)=eN2πi?=cos(N2πk?)+isin(N2πk?)
发现 W N W_{N} WN?是一个周期为 N N N的函数。

由此,我们发现变换后的函数 X ( k ) X(k) X(k)是由 N N N个周期为 N N N的函数的幂次,乘上一个系数,相加而成。且这 N N N个函数的实部和虚部都是三角函数( c o s cos cos s i n sin sin)。

我们很轻易地发现了 W N ( k ) = W N ( k + N ) W_{N}(k)=W_{N}(k+N) WN?(k)=WN?(k+N),但是这对我们来说不重要。更重要的性质源于三角函数的另一个性质——对称性。
我们把 [ 0 , 2 π ] [0,2\pi] [0,2π]分成两部分 [ 0 , π ) [0,\pi) [0,π),和 ( π , 2 π ] (\pi,2\pi] (π,2π],可以发现我们算出左边 t t t 处的值,就可以得到右边 π + t \pi +t π+t处的值。
说白了就是 s i n ( t ) = ? s i n ( π + t ) sin(t)=-sin(\pi +t) sin(t)=?sin(π+t) c o s ( t ) = ? c o s ( π + t ) cos(t)=-cos(\pi +t) cos(t)=?cos(π+t)

对应到上面就得到了一个结论,我们只要算出了 W N ( t ) W_{N}(t) WN?(t),就可以得到 W N ( N 2 + t ) W_{N}(\frac{N}{2}+t) WN?(2N?+t)。即 W N ( t ) = ? W N ( N 2 + t ) W_{N}(t)=-W_{N}(\frac{N}{2}+t) WN?(t)=?WN?(2N?+t)

上面只是简单的性质推导,正式的傅里叶变换在下面。

总而言之就是要分治,这个式子可以和分治有机结合。(我也不知道他怎么想出来的,公式推导在《数字信号处理》第四版的P122)
我们把枚举的奇数项和偶数项分离开。
X ( k ) = ∑ n 为 偶 数 x ( n ) W N n ( k ) + ∑ n 为 奇 数 x ( n ) W N n ( k ) = ∑ r = 0 N 2 ? 1 x ( 2 r ) W N 2 r ( k ) + ∑ r = 0 N 2 ? 1 x ( 2 r + 1 ) W N 2 r + 1 ( k ) = ∑ r = 0 N 2 ? 1 x 1 ( r ) W N 2 r ( k ) + W N ( k ) ∑ r = 0 N 2 ? 1 x 2 ( r ) W N 2 r ( k ) = ∑ r = 0 N 2 ? 1 x 1 ( r ) W N 2 r ( k ) + W N ( k ) ∑ r = 0 N 2 ? 1 x 2 ( r ) W N 2 r ( k ) = X 1 ( k ) + W N ( k ) X 2 ( k ) X(k)=\sum_{n为偶数}x(n)W_{N}^{n}(k)+\sum_{n为奇数}x(n)W_{N}^{n}(k) \\=\sum_{r=0}^{\frac{N}{2}-1}x(2r)W_{N}^{2r}(k)+\sum_{r=0}^{\frac{N}{2}-1}x(2r+1)W_{N}^{2r+1}(k) \\=\sum_{r=0}^{\frac{N}{2}-1}x_{1}(r)W_{N}^{2r}(k)+W_{N}(k)\sum_{r=0}^{\frac{N}{2}-1}x_{2}(r)W_{N}^{2r}(k) \\=\sum_{r=0}^{\frac{N}{2}-1}x_{1}(r)W_{\frac{N}{2}}^{r}(k)+W_{N}(k)\sum_{r=0}^{\frac{N}{2}-1}x_{2}(r)W_{\frac{N}{2}}^{r}(k) \\=X_{1}(k)+W_{N}(k)X_{2}(k) X(k)=n?x(n)WNn?(k)+n?x(n)WNn?(k)=r=02N??1?x(2r)WN2r?(k)+r=02N??1?x(2r+1)WN2r+1?(k)=r=02N??1?x1?(r)WN2r?(k)+WN?(k)r=02N??1?x2?(r)WN2r?(k)=r=02N??1?x1?(r)W2N?r?(k)+WN?(k)r=02N??1?x2?(r)W2N?r?(k)=X1?(k)+WN?(k)X2?(k)
其中 X 1 X_{1} X1? x 1 x_{1} x1?的傅里叶变换, X 2 X_{2} X2? x 2 x_{2} x2?的傅里叶变换。
其中有一步 W N 2 r ( k ) = W N 2 r W_{N}^{2r}(k)=W_{\frac{N}{2}}^{r} WN2r?(k)=W2N?r?前面忘记证了,想必读者应该一下子就能证出来了。

到此位置,所有准备工作都做完了,容易发现只要分治做好两个小多项式的点值,大的自然就出来了。所以可以分治做。

最后式子如下。
X ( k ) = X 1 ( k ) + W N k X 2 ( k ) , k = 0 , 1 , … , N 2 ? 1 X ( k + N 2 ) = X 1 ( k ) ? W N k X 2 ( k ) , k = 0 , 1 , … , N 2 ? 1 X(k)=X_{1}(k)+W^{k}_{N}X_{2}(k),k=0,1,…,\frac{N}{2}-1 \\X(k+\frac{N}{2})=X_{1}(k)-W^{k}_{N}X_{2}(k),k=0,1,…,\frac{N}{2}-1 X(k)=X1?(k)+WNk?X2?(k),k=0,1,,2N??1X(k+2N?)=X1?(k)?WNk?X2?(k),k=0,1,,2N??1

但是有一个细节问题,计算 W N ( k ) W_{N}(k) WN?(k)的复杂度是 o ( l o g n ) o(logn) o(logn)。而在分治的每一层我们需要求 N N N W W W函数,总共分治 l o g n logn logn层,最终导致我们的复杂度变成了 o ( n l o g n l o g n ) o(nlognlogn) o(nlognlogn)
那么解决办法是前面提的 W N ( t ) = ? W N ( N 2 + t ) W_{N}(t)=-W_{N}(\frac{N}{2}+t) WN?(t)=?WN?(2N?+t),我们每层只需要计算一半的 W W W,也就是 N 2 \frac{N}{2} 2N? W W W,于是在计算 W W W上的总开销就变成了 o ( n 2 l o g n l o g n ) o(\frac{n}{2}lognlogn) o(2n?lognlogn)。有锅巴用。

然后又发现一个可以优化的地方。既然 W N ( k ) = ? W N ( N 2 + k ) W_{N}(k)=-W_{N}(\frac{N}{2}+k) WN?(k)=?WN?(2N?+k)来减少 W W W的计算次数(主要是减少 s i n sin sin c o s cos cos的计算次数),主要是来源于三角函数知道 [ 0 , π ] [0,\pi] [0,π]可以推演出 [ π , 2 π ] [\pi,2\pi] [π,2π]。这样只需要在每层算 N 2 \frac{N}{2} 2N? c o s cos cos s i n sin sin,所以我们再往下细化一层,使得每层只需要计算 N 4 \frac{N}{4} 4N? c o s cos cos s i n sin sin
那么同理,三角函数知道 [ 0 , π 2 ] [0,\frac{\pi}{2}] [0,2π?]就可以推算出 [ π 2 , 2 π ] [\frac{\pi}{2},2\pi] [2π?,2π]
所以,我们开始推 W N ( k + N 4 ) W_{N}(k+\frac{N}{4}) WN?(k+4N?)
W N ( k + N 4 ) = c o s ( 2 π k N + π 2 ) + i s i n ( 2 π k N + π 2 ) = ? s i n ( 2 π k N ) + i c o s ( 2 π k N ) W_{N}(k+\frac{N}{4})=cos(\frac{2\pi k}{N}+\frac{\pi}{2})+isin(\frac{2\pi k}{N}+\frac{\pi}{2})=-sin(\frac{2\pi k}{N})+icos(\frac{2\pi k}{N}) WN?(k+4N?)=cos(N2πk?+2π?)+isin(N2πk?+2π?)=?sin(N2πk?)+icos(N2πk?)
然后,我们发现,我们对于某个 k k k计算出的 s i n sin sin c o s cos cos函数,可以沿用至 k + N 4 k+\frac{N}{4} k+4N? k + N 2 k+\frac{N}{2} k+2N? k + 3 N 4 k+\frac{3N}{4} k+43N?
所以,在每一层,我们只需要计算 N 4 \frac{N}{4} 4N? W W W
然后复杂度被优化到了 o ( n 4 l o g n l o g n ) o(\frac{n}{4}lognlogn) o(4n?lognlogn)。仍然没有任何卵用。

其实我们只需要算 W N ( 1 ) W_{N}(1) WN?(1),有 W N ( k + 1 ) = W N ( k ) × W N ( 1 ) W_{N}(k+1)=W_{N}(k)\times W_{N}(1) WN?(k+1)=WN?(k)×WN?(1),这才是真的有用。证明可以考虑这个复数的几何意义,就是把位于圆心的单位圆划等分成了 N N N个部分,取出第一个部分就相当于是一个旋转矩阵,幂次就是旋转的次数。
于是,我们只要在开始用三角函数算出 W N ( 1 ) W_{N}(1) WN?(1),后面一直乘上它就行了。

然后就做完了。

关于转换回系数,也就是逆变换,发现式子和这个差不多,所以做法也基本一样。记得最后要除以 N N N。这里就不重复介绍了。

为了方便起见,一般先把多项式长度拉到2的幂次,然后再进行变换,方便递归到底层为1。反过来说,要使用FFT板子时,注意长度应为2的幂次。
虽然是分治,但为了常数考虑,一般都用非递归版的写法。

递归版
递归版常数很大,强烈建议找个常数小的非递归版代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const double pi = acos(-1);
const int MAXN = 3000005;
struct cp
{
    double s,v;
    cp(double ss=0,double vv=0){
        s=ss;v=vv;
    }
};
cp operator * (cp a,cp b)
{
    return cp(a.s*b.s-a.v*b.v,a.s*b.v+a.v*b.s);
}
cp operator + (cp a,cp b)
{
    return cp(a.s+b.s,a.v+b.v);
}
cp operator - (cp a,cp b)
{
    return cp(a.s-b.s,a.v-b.v);
}
cp operator / (cp a,int b)
{
    return cp(a.s/b,a.v/b);
}

//预处理出2的幂次的cos和sin
double coss[MAXN],sinn[MAXN];

void fft(cp *a,int len,int opt)
{
    if(len==1)return ;
    static cp b[MAXN];
    int mid=len>>1,mid_2=mid>>1;
    for(int i=0;i<len;i++)b[(i>>1)|(i&1?mid:0)]=a[i];
    for(int i=0;i<len;i++)a[i]=b[i];
    fft(a,mid,opt);fft(a+mid,mid,opt);
    cp uni(coss[mid],opt*sinn[mid]),w(1,0);
    for(int k=0;k<len;k++)
    {
        if(k<mid)b[k]=a[k]+w*a[k+mid];
        else b[k]=a[k-mid]+w*a[k];
        w=w*uni;
    }
    for(int i=0;i<len;i++)a[i]=b[i];
}
void mul(cp *a,cp *b,int len)
{
    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<len;i++)a[i]=a[i]/len;
}
string sa,sb,sans;
cp a[MAXN],b[MAXN];
int ans[MAXN];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>sa>>sb;
    int n=sa.size(),m=sb.size();
    int len=1;
    while(len<n+m-1)coss[len]=cos(pi/len),sinn[len]=sin(pi/len),len<<=1;
    for(int i=0;i<len;i++)
    {
        a[i]=i<n?cp(sa[n-i-1]-'0',0):cp(0,0);
        b[i]=i<m?cp(sb[m-i-1]-'0',0):cp(0,0);
    }
    
    mul(a,b,len);
    
    for(int i=0;i<len;i++)
    {
        ans[i]+=round(a[i].s);
        ans[i+1]+=ans[i]/10;
        ans[i]%=10;
    }
    int flag=0;
    for(int i=len-1;i>=0;i--)
    {
        if(ans[i]==0&&flag==0)continue;
        else
        {
            flag=1;
            sans+=(char)(ans[i]+'0');
        }
    }
    cout<<sans<<endl;
    return 0;
}

非递归版

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 11:02:21  更:2022-04-23 11:02:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:20:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码