FFT算法实现,python,Java

FFT算法实践报告

FFT基本原理

代码链接: link.

DFT

在讨论FFT之前,我们需要先了解以下DFT。所谓的DFT其实就是两个矩阵做点乘。
多项式可以有两种表示方法,一种是系数表示法,另一种是点值表示法。
这两种表示法之间是可以转换的,系数表示法到点值表示法非常简单,就是随便取几个点带入求值,而点值表示法到系数表示法就需要用到插值法,关于插值法又不在本文的讨论范围之内了。
所谓的点值表示法,设A(x)是一个n阶的多项式,那么至少可以用n+1对(x0,A(x0)),(x1,A(x1))…这样的点对来表示,也就是说确定了这n+1个点,就可以唯一的确定一个多项式,形如a0+a1x+a2x²+…这种。证明的方法利用到了幼儿园级别的线性代数知识。
当两个多项式相乘时,我们可以很自然的想到将对应的点值相乘,这样就得到了结果的点值表示法。
假设现在有两个n阶的多项式相乘,那么利用点值表示法,我们需要将对应的n个点分别相乘,也就是要做n*n次乘法运算,这个时间复杂度是O(n²),与我们直接将系数表示下的多项式相乘复杂度一致。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WRL2KX6n-1636098095675)(imgs/2021-11-05-13-49-37.png)]

这已经是离散傅里叶变化的思想了。
所谓离散傅里叶变换就是
在这里插入图片描述

其本质就是M矩阵和X向量做点乘。

FFT

我们想要找到突破口就需要在将多项式转化为点值表示这一步做一点小操作。这一点操作就是利用对称性,减少计算量。
我们注意到一个n阶多项式A(x) = a0+a1x²+a2x³+…其奇数项是奇函数,偶数项是偶函数。而奇数项又可以分离成x乘一个偶函数。
因此,我们利用偶函数的性质可以减少很多计算量。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z9kKw3rA-1636098095677)(imgs/2021-11-05-14-57-27.png)]

比方现在有一个多项式x+x²+x³,那么我们应该至少需要4对点才可以求得该多项式的点值表示法,我可以找x1,x2,-x1,-x2这样的正负对,所以只需要找2对即可,因为另一半的值就无非就是符号不同。
但是只是这样的一次对称只能减少一半的计算量,只能让原本O(n²)的算法变为O(n²/2),其提升不是很大。如果可以一直利用这种对称性就好了,这种对称性建立的前提是正负对的存在,但是显然在实数域我们无法做到这一点,所以必须要在复数域讨论。
所以Fourier提出,对于一个n-1阶的多项式,我们不妨直接用n个复数w0,w1,w2…wn代替原来的x0,x1,x2…xn,这样得到一种特殊的点值表示法就是被成为离散傅里叶变化(DFT)。而这n个复数不是随便选取的,而是在复平面将单位圆等分n分后对应的点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zRol9XNK-1636098095678)(imgs/2021-11-05-14-29-04.png)]

单位根的性质复平面上的单位根有这样一个性质
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AWgPl5Ge-1636098095678)(imgs/2021-11-05-14-42-24.png)]

于是正负对出现了。
我们的程序就可以按照这个框架来写了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MrnFDez7-1636098095679)(imgs/2021-11-05-14-57-59.png)]

python实现

DFT

#DFT,本质就是两个矩阵相乘
def DFT(x):
    x = np.asarray(x,dtype=float)
    N = x.shape[0]
    M = [[j for i in range(N)]for j in range(N)]
    M = np.asarray(M,dtype=complex)
    w = np.exp(-2j*np.pi/N)

    for i in range(N):
        for j in range(N):
            M[i][j] = np.power(w,i*j)

    return np.dot(M,x)

FFT

递归
#递归FFT,利用分治思想的dft
def fft_recurrence(x):
    x = np.asarray(x,dtype=float)
    N = x.shape[0]

    if N<2:
        return DFT(x)

    x_even = fft_recurrence(x[0::2])
    x_odd = fft_recurrence(x[1::2])
    factor = np.exp(-2j * np.pi * np.arange(N) / N)

    return np.concatenate([x_even + factor[:int(N / 2)] * x_odd,
                           x_even + factor[int(N / 2):] * x_odd])

非递归

上面那种FFT是最基础的,同时因为递归的原因执行速度很慢,所以提出了非递归的优化方法。
观察 n =4时候的子序列位置变换情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i3Taxv6P-1636098095679)(imgs/2021-11-05-15-11-31.png)]

我们发现子序列最终的位置其实就是初始位置的二进制翻转后结果,如3(11)变为了3(11),而1(01)变为了2(10)。

Java实现

Complex复数类

首先创建一个复数类,定义一系列的加减乘除操作。

package fft;

public class Complex {
   
	private double realPart;
	private double imaginaryPart;
	
	public Complex() {
   
		this.realPart =0;
		this.imaginaryPart =0;
	}
	
	public Complex(double realPart,double imaginaryPart) {
   
		this.realPart = realPart;
		this.imaginaryPart = imaginaryPart;
	}
	
	/**
	 * 加法运算
	 * @param w
	 * @return
	 */
	public Complex add(Complex w) {
   
		if (w ==null) {
   
			return new Complex();
		}
		return new Complex(this.realPart+w.getRealPart(),this.imaginaryPart+w.getImaginaryPart());
	}
	
	
	/**
	 * 减法运算
	 * @param w
	 * @return
	 */
	public Complex subt(Complex w) {
   
		if (w ==null) {
   
			return new Complex();
		}
		return new Complex(this.realPart-w.getRealPart(),this.imaginaryPart-w.getImaginaryPart());
	}
	
	 /**
	  *乘法 
	  * @param w
	  * @return
	  */
	public Complex mult(Complex w) {
   
		if (w ==null) {
   
			return new Complex();
		}
		return new Complex(this.realPart*w.getRealPart()-this.imaginaryPart*w.getImaginaryPart(),
				this.realPart*w.getImaginaryPart()+this.imaginaryPart*w.getRealPart());
	}


	/**
	 * 除法
	 * @param w
	 * @return
	 */
	public Complex divide(Complex w) {
   
		if (w ==null) {
   
			return new Complex();
		}
		double W = w.getImaginaryPart()*w.getImaginaryPart() - w.getRealPart()*w.getRealPart();
		return new Complex((this.realPart*w.getRealPart()+this.imaginaryPart*w.getImaginaryPart())/W,
				(this.imaginaryPart*w.getRealPart(
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于8的FFT算法Python代码实现: ``` import math def twiddle_factor(N): W = [] for i in range(N): w = complex(math.cos(2*math.pi*i/N), -math.sin(2*math.pi*i/N)) W.append(w) return W def fft_8(x): N = len(x) if N == 1: return x else: x0 = x[::8] x1 = x[1::8] x2 = x[2::8] x3 = x[3::8] x4 = x[4::8] x5 = x[5::8] x6 = x[6::8] x7 = x[7::8] y0 = fft_8(x0) y1 = fft_8(x1) y2 = fft_8(x2) y3 = fft_8(x3) y4 = fft_8(x4) y5 = fft_8(x5) y6 = fft_8(x6) y7 = fft_8(x7) W = twiddle_factor(N) y = [0]*N for i in range(N//8): j = 8*i y[j] = y0[i] + y1[i]*W[j] + y2[i]*W[2*j] + y3[i]*W[3*j] + y4[i]*W[4*j] + y5[i]*W[5*j] + y6[i]*W[6*j] + y7[i]*W[7*j] y[j+1] = y0[i] + y1[i]*W[j+1] + y2[i]*W[2*(j+1)] + y3[i]*W[3*(j+1)] + y4[i]*W[4*(j+1)] + y5[i]*W[5*(j+1)] + y6[i]*W[6*(j+1)] + y7[i]*W[7*(j+1)] y[j+2] = y0[i] + y1[i]*W[j+2] + y2[i]*W[2*(j+2)] + y3[i]*W[3*(j+2)] + y4[i]*W[4*(j+2)] + y5[i]*W[5*(j+2)] + y6[i]*W[6*(j+2)] + y7[i]*W[7*(j+2)] y[j+3] = y0[i] + y1[i]*W[j+3] + y2[i]*W[2*(j+3)] + y3[i]*W[3*(j+3)] + y4[i]*W[4*(j+3)] + y5[i]*W[5*(j+3)] + y6[i]*W[6*(j+3)] + y7[i]*W[7*(j+3)] y[j+4] = y0[i] + y1[i]*W[j+4] + y2[i]*W[2*(j+4)] + y3[i]*W[3*(j+4)] + y4[i]*W[4*(j+4)] + y5[i]*W[5*(j+4)] + y6[i]*W[6*(j+4)] + y7[i]*W[7*(j+4)] y[j+5] = y0[i] + y1[i]*W[j+5] + y2[i]*W[2*(j+5)] + y3[i]*W[3*(j+5)] + y4[i]*W[4*(j+5)] + y5[i]*W[5*(j+5)] + y6[i]*W[6*(j+5)] + y7[i]*W[7*(j+5)] y[j+6] = y0[i] + y1[i]*W[j+6] + y2[i]*W[2*(j+6)] + y3[i]*W[3*(j+6)] + y4[i]*W[4*(j+6)] + y5[i]*W[5*(j+6)] + y6[i]*W[6*(j+6)] + y7[i]*W[7*(j+6)] y[j+7] = y0[i] + y1[i]*W[j+7] + y2[i]*W[2*(j+7)] + y3[i]*W[3*(j+7)] + y4[i]*W[4*(j+7)] + y5[i]*W[5*(j+7)] + y6[i]*W[6*(j+7)] + y7[i]*W[7*(j+7)] return y ``` 这段代码实现了基于8的FFT算法,输入的是长度为8的复数序列。其中 twiddle_factor 函数生成旋转因子,fft_8 函数对输入进行递归计算并输出长度为8的DFT结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值