FFT的实现

在处理拉曼光谱数据时,用到了傅里叶变换降噪,于是编程实现了FFT,具体实现用的是Fortran,参考了算法导论非递归的方法,此方法较递归方法,节省了复制数组的时间,时间多项式系数小于递归方法。

原理

FFT采用了分治的方法,具体为以下的等式
F [ x ] = F 0 [ x 0 ] + ω F 1 [ x 1 ] F[x]=F^0[x_0]+\omega F^1[x_1] F[x]=F0[x0]+ωF1[x1]
其中: x 0 = { x 0 , x 2 , x 4 , …   } x_0=\{x_0,x_2,x_4,\dots\} x0={x0,x2,x4,}, x 1 = { x 1 , x 3 , x 5 , …   } x_1=\{x_1,x_3,x_5,\dots\} x1={x1,x3,x5,}
变换 F 0 [ x 0 ] = [ 1 , ω 2 , ω 4 , …   ] x 0 T F^0[x_0]=[1,\omega^2,\omega^4,\dots]x_0^T F0[x0]=[1,ω2,ω4,]x0T
采用这种方法可以将时间复杂度降到 O ( N log ⁡ 2 N ) O(N\log_2N) O(Nlog2N)

实现

由上述等式可以看出FFT只适用于数据量为2的指数的情况,虽然目前已经发展出了其他的情况,如3可以表示为,
F [ x ] = F 0 [ x 0 ] + ω F 1 [ x 1 ] + ω 2 F 2 [ x 2 ] F[x]=F^0[x_0]+\omega F^1[x_1]+\omega ^2F^2[x_2] F[x]=F0[x0]+ωF1[x1]+ω2F2[x2]
如果不是2的指数则会出错,具体为数组越界和内存泄漏。《算法导论》中介绍的非递归方法首先是采用了,先将原数组进行逆序排列,

    integer function rev(a,b)
        implicit none
        integer,intent(in)::a,b
        integer(kind=1)::temp(b)
        integer::i,a_
        temp(:)=0
        a_=a-1
        do i=1,b
            if (a_==0) then
                exit
            elseif (mod(a_,2)==0) then
                a_=a_/2
                temp(i)=0
            else
                temp(i)=1
                a_=(a_-1)/2
            endif
        enddo
        rev=1
        do i=1,b
            rev=rev+2**(b-i)*temp(i)
        enddo
        return
    end function
    subroutine rev_array(a,b,l)
        implicit none
        integer,intent(in)::l
        real,intent(in)::a(l)
        real,intent(inout)::b(l)
        integer::i,temp,u
        u=nint(log(real(l))/log(2.0))
        do i=1,l
            temp=rev(i,u)
            if (temp<10) then
                print *,temp,u
            endif
            b(i)=a(temp)
        enddo
    endsubroutine

之后便可以用循环的方式实现FFT,这里将实部和虚部分开存储,以便之后的逆变换。

subroutine fft(a,b_cos,b_sin,l)
        implicit none
        integer,intent(in)::l
        real,intent(in)::a(l)
        real,intent(inout)::b_cos(l),b_sin(l)
        integer::i,j,k,u,m
        real::omega_r,omega_i,o_r,o_i,t_r,t_i,u_r,u_i
        call rev_array(a,b_cos,l) 
        b_sin(:)=0
        u=nint(log(real(l))/log(2.0))
        do i=1,u
            m=2**i
            omega_r=cos(2*pi/m)
            omega_i=sin(2*pi/m)
            do j=1,l-1,m
                o_r=1.0
                o_i=0.0
                do k=0,int(m/2)-1
                    t_r=o_r*b_cos(k+j+int(m/2))-o_i*b_sin(k+j+int(m/2))
                    t_i=o_i*b_cos(k+j+int(m/2))+o_r*b_sin(k+j+int(m/2))
                    u_r=b_cos(k+j)
                    u_i=b_sin(k+j)
                    b_cos(k+j)=u_r+t_r
                    b_sin(k+j)=u_i+t_i
                    b_cos(k+j+int(m/2))=u_r-t_r
                    b_sin(k+j+int(m/2))=u_i-t_i
                    call complex_muti(o_r,o_i,omega_r,omega_i)
                enddo
            enddo
        enddo     
    end subroutine

逆变换如下:

    subroutine rfft(a,b_cos,b_sin,l)
        implicit none
        integer,intent(in)::l
        real,intent(inout)::a(l)
        real,intent(in)::b_cos(l),b_sin(l)
        real::b_(l)
        integer::i,j,k,u,m
        real::omega_r,omega_i,o_r,o_i,t_r,t_i,u_r,u_i
        call rev_array(b_cos,a,l)
        call rev_array(b_sin,b_,l)  
        u=nint(log(real(l))/log(2.0))
        do i=1,u
            m=2**i
            omega_r=cos(2*pi/m)
            omega_i=-sin(2*pi/m)
            do j=1,l-1,m
                o_r=1.0
                o_i=0.0
                do k=0,int(m/2)-1
                    t_r=o_r*a(k+j+int(m/2))-o_i*b_(k+j+int(m/2))
                    t_i=o_i*a(k+j+int(m/2))+o_r*b_(k+j+int(m/2))
                    u_r=a(k+j)
                    u_i=b_(k+j)
                    a(k+j)=u_r+t_r
                    b_(k+j)=u_i+t_i
                    a(k+j+int(m/2))=u_r-t_r
                    b_(k+j+int(m/2))=u_i-t_i
                    call complex_muti(o_r,o_i,omega_r,omega_i)
                enddo
            enddo
        enddo
        a=a/l     
    end subroutine
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值