【第四章·FFT理论篇】深入理解FFT快速傅里叶变换原理及其应用:重叠相加法和重叠保留法计算卷积

对应程佩青《数字信号处理》第四章 快速傅里叶变换,文章全部为原创,其中独创性地研究了在推导N点FFT过程中按时间奇偶分解后只得到的两个N/2点FFT相加只能得到N/2点FFT(N点FFT的前半部分)的原因。真正从数字信号处理的角度从DFT出发推导出了FFT。

原理理解不易,建议点赞收藏后结合书本学习🤞。
请添加图片描述


DFT算法的瓶颈

分析DFT计算式 X ( k ) = ∑ n = 0 N − 1 x ( n ) W N n k , k = 0 , 1 , 2 , ⋯   , N − 1 X(k)=\sum_{n=0}^{N-1}x(n)W_N^{nk},k=0,1,2,\cdots,N-1 X(k)=n=0N1x(n)WNnk,k=0,1,2,,N1
对于序列长度为 N N N x ( n ) x(n) x(n),计算一个 X ( k ) X(k) X(k)需要进行的运算:

  • N N N次(复数)乘法
  • N − 1 N-1 N1次(复数)加法( N N N个数相加, N − 1 N-1 N1个符号)

计算全部 N N N X ( k ) X(k) X(k) k ∈ [ 0 , N − 1 ] k\in[0,N-1] k[0,N1])的运算量:

N 2 N^2 N2次乘法 与 N ( N − 1 ) N(N-1) N(N1)次加法


库利-图基算法(DIT基-2FFT)

按(时间)奇偶抽选的加速的DFT算法

要求:序列 x ( n ) x(n) x(n)长度为 N = 2 L N=2^L N=2L,即有2的幂次方个点。若不足补零。

思想:分治,将一次DFT分解成两个DFT的运算(一个 N N N点DFT → \rightarrow 两个 N 2 \frac{N}{2} 2N点DFT运算+一次蝶形运算)

算法原理

综述

通过将 N N N点DFT分解成小点数 N ’ N’ N点的DFT, N ’ N’ N点DFT一次求出所有的 X ’ ( k ) , X’(k), X(k), k ∈ [ 0 , N ’ − 1 ] k\in[0,N’-1] k[0,N1],利用周期性可以求出所有的 X ( k ) X(k) X(k)。最终分解到2点DFT,再利用 W N W_N WN的周期性质一层层往回代求出所有的 X ( k ) X(k) X(k)

引理

序列按照时间奇偶分解

请添加图片描述

W N n k W_N^{nk} WNnk的性质

回顾DFT中的定义可以得到特殊值

  • W N N / 2 = − 1 W_N^{N/2}=-1 WNN/2=1
  • W N k + N 2 = − W N k W_N^{k+\frac{N}{2}}=-W_N^k WNk+2N=WNk
    并且需要注意到 W N n k W_N^{nk} WNnk的可约性和周期性

推导

序列分解

点数 N = 2 L N=2^L N=2L的序列 x ( n ) x(n) x(n)按照奇偶分解,得到长度为 N 2 \frac{N}{2} 2N的偶序列 x 1 ( r ) x_1(r) x1(r)和奇序列 x 2 ( r ) x_2(r) x2(r),其中 x 1 ( r ) = x ( 2 r ) , x 2 ( r ) = x ( 2 r + 1 ) , r = 0 , 1 , ⋯   , N 2 − 1 x_1(r)=x(2r),x_2(r)=x(2r+1),r=0,1,\cdots,\frac{N}{2}-1 x1(r)=x(2r),x2(r)=x(2r+1),r=0,1,,2N1

分解后的序列的DFT

回顾DFT的线性性质,两个长度相等均为 N N N的序列之和的N点DFT等于两个N点DFT的和,需要保证三者的点数相等才有意义。
但此处的分解并不是将相同点的值进行拆分,而是将序列按照位置奇偶顺序进行重排

考虑对奇偶分解后的序列进行DFT,由于分解后的两个序列都是 N 2 \frac{N}{2} 2N点的,因此原始 N N N点序列的DFT也只有 N 2 \frac{N}{2} 2N点,即前半部分。

x ( n ) x(n) x(n) N N N点DFT,就能根据 n n n的奇偶将DFT分解(重新排列)成两个 N 2 \frac{N}{2} 2N点长的序列的“ N N N点”(这里不符合DFT的定义,从DFS的角度来看只有 N 2 \frac{N}{2} 2N个独立的谐波分量)DFT序列之和
X ( k ) = D F T [ x ( n ) ] = ∑ n = 0 N − 1 x ( n ) W N n k = ∑ n = 0 N − 1 x ( n ) W N n k ⏟ n 为偶数 + ∑ n = 0 N − 1 x ( n ) W N n k ⏟ n 为奇数 = ∑ r = 0 N 2 − 1 x ( 2 r ) W N 2 r k + W N k ∑ r = 0 N 2 − 1 x ( 2 r + 1 ) W N 2 r k = ∑ r = 0 N 2 − 1 x 1 ( r ) W N r k 1 + W N k ∑ r = 0 N 2 − 1 x 2 ( r ) W N r k 1 , k 1 = 2 k = 0 , 2 , 4 , ⋯   , 2 ( N − 1 ) = ∑ r = 0 N 2 − 1 x 1 ( r ) W N / 2 r k ⏟ N 2 点长序列 + W N k ∑ r = 0 N 2 − 1 x 2 ( r ) W N / 2 r k ⏟ N 2 点长序列 , k = 0 , 1 , 2 , ⋯   , N − 1 \begin{aligned}X(k)=&DFT[x(n)]=\sum_{n=0}^{N-1}x(n)W_N^{nk}=\begin{matrix} \underbrace{\sum_{n=0}^{N-1} x(n)W_N^{nk}} \\ n为偶数 \end{matrix}+\begin{matrix} \underbrace{\sum_{n=0}^{N-1} x(n)W_N^{nk}} \\ n为奇数 \end{matrix}\\=&\sum_{r=0}^{\frac{N}{2}-1}x(2r)W_N^{2rk}+W_N^k\sum_{r=0}^{\frac{N}{2}-1}x(2r+1)W_N^{2rk}\\=&\sum_{r=0}^{\frac{N}{2}-1}x_1(r)W_N^{rk_1}+W_N^k\sum_{r=0}^{\frac{N}{2}-1}x_2(r)W_N^{rk_1},k_1=2k=0,2,4,\cdots,2(N-1)\\=&\begin{matrix}\underbrace{\sum_{r=0}^{\frac{N}{2}-1}x_1(r)W_{N/2}^{rk}}\\ \frac{N}{2}点长序列 \end{matrix}+W_N^{k}\begin{matrix}\underbrace{\sum_{r=0}^{\frac{N}{2}-1}x_2(r)W_{N/2}^{rk}}\\ \frac{N}{2}点长序列 \end{matrix},k=0,1,2,\cdots,N-1\end{aligned} X(k)====DFT[x(n)]=n=0N1x(n)WNnk= n=0N1x(n)WNnkn为偶数+ n=0N1x(n)WNnkn为奇数r=02N1x(2r)WN2rk+WNkr=02N1x(2r+1)WN2rkr=02N1x1(r)WNrk1+WNkr=02N1x2(r)WNrk1,k1=2k=0,2,4,,2(N1) r=02N1x1(r)WN/2rk2N点长序列+WNk r=02N1x2(r)WN/2rk2N点长序列,k=0,1,2,,N1
经过上面代数形式的转化,可以看到原序列 N N N点DFT共有 N N N个抽样点;经过重排后两个 N 2 \frac{N}{2} 2N点序列的“ N N N点”DFT实际上只有 N 2 \frac{N}{2} 2N个抽样点,抽样间隔是 N N N点序列的两倍,这是由于时域被奇偶抽选

因此可以考虑将其写成只有 N 2 \frac{N}{2} 2N个抽样点的形式( W N n k W_N^{nk} WNnk的可约性),对于两个 N 2 \frac{N}{2} 2N点序列其抽样间隔正好等于 N N N个抽样点的两倍。

若将其写成“真正的” N 2 \frac{N}{2} 2N点DFT,抽样间隔由以 N N N点为基准变成以 N 2 \frac{N}{2} 2N点为基准,
基波改变,因此谐波分量的含义也发生了改变,
但是按照上面式子倒推回去,可以得到前半部分 X ( k ) X(k) X(k)
对于两个 N 2 \frac{N}{2} 2N点DFT, k ′ k' k包含全部的谐波分量;
但是对于 N N N点DFT, k k k只有一半的谐波分量。

注意 k k k k ’ k’ k的含义是不同的。
X ( k ) = X 1 ( k ′ ) + X 2 ( 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 ′ , k = k ′ = 0 , 1 , ⋯   , N 2 − 1 \begin{aligned}X(k)=&X_1(k')+X_2(k')\\=&\sum_{r=0}^{\frac{N}{2}-1}x_1(r)W_{N/2}^{rk'}+W_N^{k}\sum_{r=0}^{\frac{N}{2}-1}x_2(r)W_{N/2}^{rk'},k=k'=0,1,\cdots,\frac{N}{2}-1\end{aligned} X(k)==X1(k)+X2(k)r=02N1x1(r)WN/2rk+WNkr=02N1x2(r)WN/2rk,k=k=0,1,,2N1

蝶形运算

在前面代数推出的“ N 2 \frac{N}{2} 2N点序列的 N N N点DFT”伪形式中,考虑到 W N n k W_N^{nk} WNnk的周期性其实可以直接推出后半部分的 N N N点DFT,借用前半部分来表示后半部分的结果可以写作
X ( k + N 2 ) = X 1 ( k ′ + N 2 ) + X 2 ( k ′ + N 2 ) = ∑ r = 0 N 2 − 1 x 1 ( r ) W N / 2 r k ′ + N 2 + W N k ∑ r = 0 N 2 − 1 x 2 ( r ) W N / 2 N 2 − 1 x 2 ( r ) W N / 2 r k ′ + N 2 = ∑ 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 ′ , k = k ′ = 0 , 1 , ⋯   , N 2 − 1 \begin{aligned}X(k+\frac{N}{2})=&X_1(k'+\frac{N}{2})+X_2(k'+\frac{N}{2})\\=&\sum_{r=0}^{\frac{N}{2}-1}x_1(r)W_{N/2}^{rk'+\frac{N}{2}}+W_N^k\sum_{r=0}^{\frac{N}{2}-1}x_2(r)W_{N/2}^{\frac{N}{2}-1}x_2(r)W_{N/2}^{rk'+\frac{N}{2}}\\=&\sum_{r=0}^{\frac{N}{2}-1}x_1(r)W_{N/2}^{rk'}-W_N^{k}\sum_{r=0}^{\frac{N}{2}-1}x_2(r)W_{N/2}^{rk'},k=k'=0,1,\cdots,\frac{N}{2}-1\end{aligned} X(k+2N)===X1(k+2N)+X2(k+2N)r=02N1x1(r)WN/2rk+2N+WNkr=02N1x2(r)WN/22N1x2(r)WN/2rk+2Nr=02N1x1(r)WN/2rkWNkr=02N1x2(r)WN/2rk,k=k=0,1,,2N1
k + N 2 = N 2 , N 2 + 1 , ⋯   , N − 1 k+\frac{N}{2}=\frac{N}{2},\frac{N}{2}+1,\cdots,N-1 k+2N=2N,2N+1,,N1,为后半部分结果

将前半部分和后半部分相加得到完整的 N N N点DFT。这一步骤称作蝶形运算。

考虑到 W N k W_N^k WNk的值已经计算,存储 W N k X 2 ( k ) W_N^kX_2(k) WNkX2(k)的计算结果,只需进行一次乘法运算和两次加法运算

思考 分解后的两个序列包含 x ( n ) x(n) x(n)的全部信息,对“分解后的原序列”做DFT为什么只能得到前半部分的DFT?
  • 深刻理解 X ( k ′ ) X(k') X(k) k ′ k' k的含义, k ′ k' k代表第 k ′ k' k个谐波分量,为基波 2 π N ′ \frac{2\pi}{N'} N2π k ′ k' k倍。而上式中序列长度为 N 2 \frac{N}{2} 2N个点,频域抽样点也为 N ′ = N 2 N'=\frac{N}{2} N=2N,因此代表谐波的 k k k是与基波参量—序列长度 N N N分不开的,只有知道了序列的长度 N N N,DFT的第 k k k个频率分量才有含义。
  • N N N点长序列的 N N N点DFT分解成两个 N 2 \frac{N}{2} 2N点的 N N N点DFT这一说法是不严谨的,根据定义式分解的 N 2 \frac{N}{2} 2N点序列只有 N 2 \frac{N}{2} 2N个独立的频率分量,后半部分的频率分量是重叠的,但是根据周期性可以求出后半部分。
  • N N N点DFT重排成两个 N 2 \frac{N}{2} 2N点序列的 N N N点DFT,后者其实已经包含了 x ( n ) x(n) x(n)的全部信息,但是由于DFT定义的限制,后半部分的频率是混叠的,并且此时的谐波分量均是针对长度为 N N N的序列的,在重排时奇数和偶数下标之间的时域间隔导致了长度为 N N N意义下的频率间隔的翻倍。但若写成 N 2 \frac{N}{2} 2N点序列的 N 2 \frac{N}{2} 2N点DFT,该间隔就成了 N 2 \frac{N}{2} 2N点DFT意义下的“标准”间隔。
  • 为了符合DFT的定义,选择 N 2 \frac{N}{2} 2N点DFT的间隔为尺度去研究 N N N点序列的DFT,这里的尺度是分解后的两个 N 2 \frac{N}{2} 2N点序列的, N N N点序列的 N N N点DFT的间隔仍保持不变。但是必须将 N N N点DFT重排成两个 N 2 \frac{N}{2} 2N点序列的 N N N点DFT的表达式的 N 2 \frac{N}{2} 2N尺度下的后半部分的频率舍去。对于 N 2 \frac{N}{2} 2N点DFT这一尺度下的DFT已经包含了它自己全部的频率分量,但是对于 x ( n ) x(n) x(n) N N N点DFT,后半部分的频率分量成为了规范定义下的损失,所幸这些混叠频率可以用周期性轻松求得。

算法流程

N N N点序列分解成序号为奇数和序号为偶数的序列,
然后对序列进行 N 2 \frac{N}{2} 2N点DFT求出前半部分的 N N N点DFT( N 2 \frac{N}{2} 2N个频率),
再利用蝶形运算求出后半部分的 N N N点DFT。
这样子进行下去直到分解成2点DFT,再依次返回计算结果。
请添加图片描述

尝试画出输出自然序,输入倒位序的流图
请添加图片描述

FFT运算量

由流图可以看出一共有 L = l o g 2 N L=log_2N L=log2N层蝶形运算,
每层共有 N 2 \frac{N}{2} 2N个蝶形运算

总(复数)乘法: N 2 l o g 2 N \frac{N}{2}log_2N 2Nlog2N
总(复数)加法: N l o g 2 N Nlog_2N Nlog2N

FFT存储容量

对于同址运算(蝶形运算输出的两个值放进输入的两个存储器中),保存输入序列需要 N N N个存储单元,
保存系数需要 N 2 \frac{N}{2} 2N个存储单元,一共需要 3 N 2 \frac{3N}{2} 23N个存储单元

FFT分段处理计算卷积

一个长度很长的序列 x ( n ) x(n) x(n)与一个短的有限长序列 h ( n ) h(n) h(n)卷积,整体计算性能差,
考虑将 x ( n ) x(n) x(n)分段与 h ( n ) h(n) h(n)卷积,再将结果按照一定顺序相加得到完整结果。

x ( n ) x(n) x(n)相同的分段下,采用第三章中的不同点数的DFT来计算快速卷积会产生不同的结果,处理的方式也有所不同;
按照圆周卷积点数及结果的不同,分为重叠相加法和重叠保留法。

重叠相加法(圆周卷积不重叠法)

将长度为 L L L的序列 x ( n ) x(n) x(n)每段序列尽可能均分成长度为 N N N(最后一段补零至 N N N)的各段 x i ( n ) x_i(n) xi(n)
各段依次与长度为 M M M的序列 h ( n ) h(n) h(n)(圆周)卷积(使用FFT方法)得到各段 x i ( n ) x_i(n) xi(n)的与 h ( n ) h(n) h(n)的线性卷积 y i ( n ) y_i(n) yi(n)
y i ( n ) y_i(n) yi(n)拼接成 y ( n ) = x ( n ) ∗ h ( n ) y(n)=x(n)*h(n) y(n)=x(n)h(n)

为方便计算,取各段 x i ( n ) x_i(n) xi(n)从0开始, x i ( n ) = x ( n + i N ) R N ( n ) , i = 0 , 1 , ⋯ x_i(n)=x(n+iN)R_N(n),i=0,1,\cdots xi(n)=x(n+iN)RN(n),i=0,1, y i ( n ) = x i ( n ) ∗ h ( n ) y_i(n)=x_i(n)*h(n) yi(n)=xi(n)h(n)
y i ( n ) y_i(n) yi(n)的点数为 L ′ = N + M − 1 L'=N+M-1 L=N+M1 对应 n = 0 , 1 , ⋯   , N + M − 2 n=0,1,\cdots,N+M-2 n=0,1,,N+M2
y ( n ) = ∑ i = 0 ∞ y i ( n − i N ) ⋯ 拼接方法 , 重叠 = ∑ i = 0 ∞ x i ( n − i N ) ∗ h ( n ) \begin{aligned}y(n)=&\sum_{i=0}^{\infty}y_i(n-iN)&\cdots 拼接方法,重叠\\=&\sum_{i=0}^{\infty}x_i(n-iN)*h(n)\end{aligned} y(n)==i=0yi(niN)i=0xi(niN)h(n)拼接方法,重叠
圆周卷积点数 N ′ ≥ L ′ = N + M − 1 N'\ge L'=N+M-1 NL=N+M1,圆周卷积不发生重叠,线性卷积直接由圆周卷积得到。

可直接由上面的公式求出 y ( n ) y(n) y(n)
由于各段线性卷积之间重叠 M − 1 M-1 M1个点
将其相加故称作重叠相加法。

因此“重叠”是指由圆周卷积得到的各段线性卷积按照一定顺序将其位置错开相加时每段有重合的部分。
而分段 x i ( n ) x_i(n) xi(n)并没有重叠。

重叠保留法(N点圆周卷积重叠法)

由圆周卷积得到的各段线性卷积按照一定顺序将其位置错开相加时没有重合的部分,
即(有效部分,需要舍去前 M − 1 M-1 M1个点)首尾相连。

圆周卷积点数 N ′ N' N取与 x i ( n ) x_i(n) xi(n)点数相等 N ′ = N < L ′ N'=N<L' N=N<L
圆周卷积发生重叠,混叠部分为 n = 0 , 1 , ⋯   , M − 2 n=0,1,\cdots,M-2 n=0,1,,M2
M − 1 M-1 M1个点(为圆周卷积之间的重叠点数,与重叠相加法线性卷积之间相加所排列的重叠点数相同)

这意味着如果均匀划分 x ( n ) x(n) x(n)成各段,不能恢复出完整的线性卷积和,因为每一段与 h ( n ) h(n) h(n)卷积都有损失。

划分的各段 x i ( n ) x_i(n) xi(n)需要有重叠
划分的 x i ( n ) x_i(n) xi(n)包含前一段 x i − 1 ( n ) x_{i-1}(n) xi1(n)包含的元素,
通过增加划分序列的冗余来弥补圆周卷积的损失。

考虑舍去当前段的圆周卷积的前 M − 1 M-1 M1个点,得到剩下 N − M + 1 N-M+1 NM+1个点的线性卷积。
y i ( n ) = x i ( n ) ∗ h ( n ) y_i(n)=x_i(n)*h(n) yi(n)=xi(n)h(n) y i ′ ( n ) = x i ( n ) Ⓝ h ( n ) y_i'(n)=x_i(n)Ⓝh(n) yi(n)=xi(n)h(n)
只有 n = M − 1 , M , M + 1 , ⋯   , N − 1 n=M-1,M,M+1,\cdots,N-1 n=M1,M,M+1,,N1的值与 y i ( n ) y_i(n) yi(n)相等,故令 y i ( n ) = { y i ′ ( n ) , M − 1 ≤ n ≤ N − 1 0 , 0 ≤ n ≤ M − 2 y_i(n)=\begin{cases}y_i'(n),M-1\le n\le N-1\\0,0\le n\le M-2\end{cases} yi(n)={yi(n),M1nN10,0nM2
下面考虑如何确定被舍去的前 M − 1 M-1 M1个圆周卷积结果所对应的正确的线性卷积结果,
作卷积计算的两个序列的哪个部分对这部分结果负责,
在这里关心 x i ′ ( n ) x_i'(n) xi(n)即可,通过它找要求的重叠部分。
x i ′ ( n ) , n ∈ [ 0 , N − 1 ] x_i'(n),n\in[0,N-1] xi(n),n[0,N1]
h ( n ) , n ∈ [ 0 , M − 1 ] h(n),n\in [0,M-1] h(n),n[0,M1]
线性卷积 y i ′ ′ ( n ) = x i ′ ( n ) ∗ h ( n ) = ∑ m = 0 N − 1 x i ′ ( m ) h ( n − m ) = ∑ m = 0 M − 2 x i ′ ( m ) h ( n − m ) , n ∈ [ 0 , M − 2 ] \begin{aligned}y_i''(n)=&x_i'(n)*h(n)\\=&\sum_{m=0}^{N-1}x_i'(m)h(n-m)\\=&\sum_{m=0}^{M-2}x_i'(m)h(n-m),n\in [0,M-2]\end{aligned} yi′′(n)===xi(n)h(n)m=0N1xi(m)h(nm)m=0M2xi(m)h(nm),n[0,M2]
若想恢复这前 M − 1 M-1 M1个点,需要的 x i ′ ( n ) x_i'(n) xi(n) M − 1 M-1 M1个点。

如果分段时重复选取 x ( n ) x(n) x(n)的当前段 x i ( n ) x_i(n) xi(n)的后 M − 1 M-1 M1个点,
作为下一段 x i + 1 ( n ) x_{i+1}(n) xi+1(n) h ( n ) h(n) h(n)圆周卷积结果前 M − 1 M-1 M1个点的补偿
(实际上是本段末尾点卷积结果正好对应,相当于补上下一段重叠而舍去的卷积);
当前段的圆周卷积能恢复出包含这部分的线性卷积。

x i ( n ) = x [ n + i ( N − m + 1 ) ] R N ( n ) x_i(n)=x[n+i(N-m+1)]R_N(n) xi(n)=x[n+i(Nm+1)]RN(n)
每一段与上一段重叠 M − 1 M-1 M1个点
x ( n ) = ∑ i = 0 ∞ x i [ n − i ( N − m + 1 ) ] x(n)=\sum_{i=0}^{\infty}x_i[n-i(N-m+1)] x(n)=i=0xi[ni(Nm+1)]
y ( n ) = x ( n ) ∗ h ( n ) = ∑ i = 0 ∞ x i [ n − i ( N − m + 1 ) ] ∗ h ( n ) = ∑ i = 0 ∞ y i [ n − i ( N − M + 1 ) ] ⋯ 拼接方法 , 不重叠 \begin{aligned}y(n)=&x(n)*h(n)\\=&\sum_{i=0}^{\infty}x_i[n-i(N-m+1)]*h(n)\\=&\sum_{i=0}^{\infty}y_i[n-i(N-M+1)]&\cdots 拼接方法,不重叠\end{aligned} y(n)===x(n)h(n)i=0xi[ni(Nm+1)]h(n)i=0yi[ni(NM+1)]拼接方法,不重叠
上式中看似 y i ( n ) y_i(n) yi(n)发生了重叠
y i ( n ) y_i(n) yi(n)的长度为 N − 1 N-1 N1
但实际上舍去前 M − 1 M-1 M1个点后有值部分长度为 N − M + 1 N-M+1 NM+1
实现了有值部分的不重叠(保留)拼接。

需要注意的在计算中为了方便,防止首段的值被错误舍去,先在 x ( n ) x(n) x(n)前补 M − 1 M-1 M1零点再进行分段计算。
带来的后果为 y ( n ) y(n) y(n)有值部分是从 n = M − 1 n=M-1 n=M1开始的,这也是需要注意的。

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重叠相加法(Overlap-add method)是一种数字信号处理中常用的算法,用于实现长信号的快速卷积。下面是用C语言实现重叠相加法的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define PI 3.14159265358979323846 // FFT实现 void fft(double *xr, double *xi, int n, int sign) { int i, j, k, m, nv2; double tr, ti, ur, ui, sr, si, tvr; m = (int)log2(n); nv2 = n / 2; j = nv2; for (i = 1; i < n - 1; i++) { if (i < j) { tr = xr[j]; ti = xi[j]; xr[j] = xr[i]; xi[j] = xi[i]; xr[i] = tr; xi[i] = ti; } k = nv2; while (k <= j) { j -= k; k /= 2; } j += k; } for (i = 1; i <= m; i++) { k = 1 << i; for (j = 0; j < nv2; j += k) { for (sr = 0.0, si = 0.0, m = j; m < j + k / 2; m++) { ur = cos(PI * (m - j) / (double)(k / 2)); ui = sign * sin(PI * (m - j) / (double)(k / 2)); tr = xr[m + k / 2] * ur - xi[m + k / 2] * ui; ti = xr[m + k / 2] * ui + xi[m + k / 2] * ur; xr[m + k / 2] = xr[m] - tr; xi[m + k / 2] = xi[m] - ti; xr[m] += tr; xi[m] += ti; } } } if (sign == -1) { for (i = 0; i < n; i++) { xr[i] /= n; xi[i] /= n; } } } // 重叠相加法实现 void overlap_add(double *x, int lx, double *h, int lh, double *y, int ly) { int n = 1; while (n < lx + lh - 1) { n *= 2; } double *xr = (double*)malloc(n * sizeof(double)); double *xi = (double*)malloc(n * sizeof(double)); double *hr = (double*)malloc(n * sizeof(double)); double *hi = (double*)malloc(n * sizeof(double)); double *yr = (double*)malloc(n * sizeof(double)); double *yi = (double*)malloc(n * sizeof(double)); int i; for (i = 0; i < n; i++) { xr[i] = 0.0; xi[i] = 0.0; hr[i] = 0.0; hi[i] = 0.0; yr[i] = 0.0; yi[i] = 0.0; } for (i = 0; i < lx; i++) { xr[i] = x[i]; } for (i = 0; i < lh; i++) { hr[i] = h[i]; } fft(xr, xi, n, 1); fft(hr, hi, n, 1); for (i = 0; i < n; i++) { yr[i] = xr[i] * hr[i] - xi[i] * hi[i]; yi[i] = xr[i] * hi[i] + xi[i] * hr[i]; } fft(yr, yi, n, -1); for (i = 0; i < ly; i++) { y[i] = yr[i]; } free(xr); free(xi); free(hr); free(hi); free(yr); free(yi); } int main() { double x[] = {1.0, 2.0, 3.0, 4.0, 5.0}; double h[] = {1.0, 2.0, 3.0}; double y[7]; overlap_add(x, 5, h, 3, y, 7); int i; for (i = 0; i < 7; i++) { printf("%f ", y[i]); } printf("\n"); return 0; } ``` 这段代码实现了重叠相加法,其中用到了FFT算法。输入信号x的长度为lx,卷积核h的长度为lh,输出信号y的长度为ly(ly = lx + lh - 1)。在main函数中,我们给出了一个示例,其中x为{1.0, 2.0, 3.0, 4.0, 5.0},h为{1.0, 2.0, 3.0},y的长度为7。输出结果为{1.0, 4.0, 10.0, 16.0, 22.0, 16.0, 9.0}。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值