精华内容
参与话题
问答
  • Matlab中快速傅里叶变换FFT结果的物理意义-Matlab中快速傅里叶变换FFT结果的物理意义.doc Matlab中快速傅里叶变换FFT结果的物理意义。 小白级解说, 新手可以看看。:lol
  • MATLAB视频教程详解快速傅里叶变换FFT在MATLAB中的实现-详解快速傅里叶变换FFT在MATLAB中的实现.pdf MATLAB视频教程:详解快速傅里叶变换FFT在MATLAB中的实现,带你全方位理解FFT变换结果
  • 快速傅里叶变换 FFT

    2018-09-25 14:05:34
    因此至DFT被发现以来,在很长的一段时间内都不能被应用到实际的工程项目中,直到一种快速的离散傅立叶计算方法——FFT,被发现,离散傅立叶变换才在实际的工程中得到广泛应用。需要强调的是,FFT并不是一种新的频域...

    在做题打比赛的时候,FFT(快速傅里叶变换)这个名词超级常见,这个算法实际上是在结合复数,多项式的点值表示等知识,加速两个多项式的乘法运算的过程

    HDU 1402

    两个高精大数的乘法可以看成两个多项式的乘积,所以可以用FFT来优化

    FFT的实际流程为: 将原多项式转化到复数空间,此空间内多项式可以直接一对一乘。乘完后再逆转化回到实数空间。此时所有的数取整就得到了最后的ans

    FFT有几个需要注意的问题:

    1. Len是两个多项式卷积后的最终多项式长度,需要为2的k次方
    2. 你需要凭借这个Len初始化复数数组:x1[0~len-1]={a[i],0} x2[0~len-1]={b[i],0}
    3. 如果两个数组是同一个,只需要进行一次DFT
    4. 数组大小开原数组最大长度x的4倍:因为len>2*x,且len为2^k
    #include <stdio.h> 
    #include <string.h> 
    #include <iostream> 
    #include <algorithm> 
    #include <math.h> 
    using namespace std;
    
    const double PI = acos(-1.0);
    //复数结构体 
    struct Complex {
    	double x, y; //实部和虚部 x+yi 
    	Complex(double _x = 0.0, double _y = 0.0) {
    		x = _x;
    		y = _y;
    	}
    	Complex operator -(const Complex &b) const {
    		return Complex(x - b.x, y - b.y);
    	}
    	Complex operator +(const Complex &b) const {
    		return Complex(x + b.x, y + b.y);
    	}
    	Complex operator *(const Complex &b) const {
    		return Complex(x * b.x - y * b.y, x * b.y + y * b.x);
    	}
    };
    /* 
     * 进行FFT和IFFT前的反转变换。 
     * 位置i和 (i二进制反转后位置)互换
     * len必须去2的幂 
     */
    void change(Complex y[], int len) {
    	int i, j, k;
    	for (i = 1, j = len / 2; i < len - 1; i++) {
    		if (i < j)
    			swap(y[i], y[j]);
    		//交换互为小标反转的元素,i<j保证交换一次 
    		//i做正常的+1,j左反转类型的+1,始终保持i和j是反转的 
    		k = len / 2;
    		while (j >= k) {
    			j -= k;
    			k /= 2;
    		}
    		if (j < k)
    			j += k;
    	}
    }
    /* 
     * 做FFT 
     * len必须为2^k形式, 
     * on==1时是DFT,on==-1时是IDFT 
     */
    void fft(Complex y[], int len, int on) {
    	change(y, len);
    	for (int h = 2; h <= len; h <<= 1) {
    		Complex wn(cos(-on * 2 * PI / h), sin(-on * 2 * PI / h));
    		for (int j = 0; j < len; j += h) {
    			Complex w(1, 0);
    			for (int k = j; k < j + h / 2; k++) {
    				Complex u = y[k];
    				Complex t = w * y[k + h / 2];
    				y[k] = u + t;
    				y[k + h / 2] = u - t;
    				w = w * wn;
    			}
    		}
    	}
    	if (on == -1)
    		for (int i = 0; i < len; i++)
    			y[i].x /= len;
    }
    const int MAXN = 200010;
    Complex x1[MAXN], x2[MAXN];
    char str1[MAXN / 2], str2[MAXN / 2];
    int sum[MAXN];
    int main() {
    	while (scanf("%s%s", str1, str2) == 2) {
    		int len1 = strlen(str1);
    		int len2 = strlen(str2);
    		int len = 1;
    		while (len < len1 * 2 || len < len2 * 2)
    			len <<= 1;
    		for (int i = 0; i < len1; i++)
    			x1[i] = Complex(str1[len1 - 1 - i] - '0', 0);
    		for (int i = len1; i < len; i++)
    			x1[i] = Complex(0, 0);
    		for (int i = 0; i < len2; i++)
    			x2[i] = Complex(str2[len2 - 1 - i] - '0', 0);
    		for (int i = len2; i < len; i++)
    			x2[i] = Complex(0, 0);
    		//求DFT 
    		fft(x1, len, 1);
    		fft(x2, len, 1);
    		for (int i = 0; i < len; i++)
    			x1[i] = x1[i] * x2[i];
    		fft(x1, len, -1);
    		for (int i = 0; i < len; i++)
    			sum[i] = (int) (x1[i].x + 0.5);
    		for (int i = 0; i < len; i++) {
    			sum[i + 1] += sum[i] / 10;
    			sum[i] %= 10;
    		}
    		len = len1 + len2 - 1;
    		while (sum[len] <= 0 && len > 0)
    			len--;
    		for (int i = len; i >= 0; i--)
    			printf("%c", sum[i] + '0');
    		printf("\n");
    	}
    	return 0;
    }
    
    

    &ThickSpace;\\\;


    &ThickSpace;\\\;

    HDU 4609

    题意: n个木棍,问取三个组成三角形的概率

    解析:

    卷积:向量{a1,a2,a3a_1,a_2,a_3}的卷积相当于多项式的乘

    首先处理出sum[i],表示两根加起来为i的方案数:
    假设原数组为:1,2,3,3,4,我们处理一个累加数组d[i]表示长度为i的有多少根,d数组为:{1,1,2,1}
    显然,d数组的卷积就是sum数组sum[k]=i=1k1d[i]d[ki]sum[k]=\sum_{i=1}^{k-1}d[i]*d[k-i]

    但是这里需要容斥两部分:取两次自己2*a=a+a,被算两次a+b=b+a,所以FFT得出的sum需要sum[2*a[i]]--,sum[j]/=2

    我开始以为虽然有的会减1再除2,和直接除2一样(偶数+1),后来WA了,因为有些是(奇数+1)

    注意数组大小一定要27000

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    
    const double PI = acos(-1.0);
    //复数结构体
    struct Complex {
    	double x, y; //实部和虚部 x+yi
    	Complex(double _x = 0.0, double _y = 0.0) {
    		x = _x;
    		y = _y;
    	}
    	Complex operator -(const Complex &b) const {
    		return Complex(x - b.x, y - b.y);
    	}
    	Complex operator +(const Complex &b) const {
    		return Complex(x + b.x, y + b.y);
    	}
    	Complex operator *(const Complex &b) const {
    		return Complex(x * b.x - y * b.y, x * b.y + y * b.x);
    	}
    };
    /*
     * 进行FFT和IFFT前的反转变换。
     * 位置i和 (i二进制反转后位置)互换
     * len必须去2的幂
     */
    void change(Complex y[], int len) {
    	int i, j, k;
    	for (i = 1, j = len / 2; i < len - 1; i++) {
    		if (i < j)
    			swap(y[i], y[j]);
    		//交换互为小标反转的元素,i<j保证交换一次
    		//i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
    		k = len / 2;
    		while (j >= k) {
    			j -= k;
    			k /= 2;
    		}
    		if (j < k)
    			j += k;
    	}
    }
    /*
     * 做FFT
     * len必须为2^k形式,
     * on==1时是DFT,on==-1时是IDFT
     */
    void fft(Complex y[], int len, int on) {
    	change(y, len);
    	for (int h = 2; h <= len; h <<= 1) {
    		Complex wn(cos(-on * 2 * PI / h), sin(-on * 2 * PI / h));
    		for (int j = 0; j < len; j += h) {
    			Complex w(1, 0);
    			for (int k = j; k < j + h / 2; k++) {
    				Complex u = y[k];
    				Complex t = w * y[k + h / 2];
    				y[k] = u + t;
    				y[k + h / 2] = u - t;
    				w = w * wn;
    			}
    		}
    	}
    	if (on == -1)
    		for (int i = 0; i < len; i++)
    			y[i].x /= len;
    }
    
    
    
    
    const int MAXN = 400010;
    Complex x1[MAXN], x2[MAXN];
    LL x[MAXN];
    LL d[MAXN];
    LL sum[MAXN];
    int main() {
        int t;scanf("%d",&t);
    
    	while (t--) {
            memset(d,0,sizeof(d));
            LL n;scanf("%lld",&n);
    
            for(int i=0;i<n;i++){
                scanf("%lld",&x[i]);d[x[i]]++;
            }
            sort(x,x+n);
    
            int len=1;
    
            while(len<2*(x[n-1]+1))len<<=1;
    
    		for (int i = 0; i < len; i++)
    			x1[i] = Complex(d[i], 0);
    			//x2[i] = Complex(d[i], 0);因为自己和自己卷积,所以只需要做一次即可
    
    		//求DFT
    		fft(x1, len, 1);
    		for (int i = 0; i < len; i++)
    			x1[i] = x1[i] * x1[i];
    		fft(x1, len, -1);
    		for (int i = 0; i < len; i++)
    			sum[i] = (LL) (x1[i].x + 0.5);
    
            for(int i=0;i<n;i++){
                sum[x[i]*2]--;
            }
    
    		for(int i=0;i<len;i++){
                sum[i] /= 2;
    		}
    
    		LL k=0;
    
    		LL all=n*(n-1ll)*(n-2ll)/6ll;
    		LL ans=all;
    
    		for(int i=0;i<len;i++){
                if(sum[i]==0)continue;
                while(x[k]<i&&k<n)k++;
                if(k==n)break;
                ans-=sum[i]*(n-k);
    		}
    
    		cout<< setiosflags(ios::fixed)<<setprecision(7) <<1.0*ans/(1.0*all)<<endl;
    	}
    	return 0;
    }
    
    
    展开全文
  • 快速傅里叶变换FFT

    千次阅读 2015-05-23 23:44:00
    快速傅里叶变换FFTDFT是信号分析与处理中的一种重要变换。但直接计算DFT的计算量与变换区间长度N的平方成正比,当N较大时,计算量太大,直接用DFT算法进行谱分析和信号的实时处理是不切实际的。 1.直接计算DFT ...

    快速傅里叶变换FFT

    DFT是信号分析与处理中的一种重要变换。但直接计算DFT的计算量与变换区间长度N的平方成正比,当N较大时,计算量太大,直接用DFT算法进行谱分析和信号的实时处理是不切实际的。
    1.直接计算DFT
    长度为N的有限长序列x(n)的DFT为:
    这里写图片描述
    2.减少运算量的思路和方法
    思路:N点DFT的复乘次数等于N2。把N点DFT分解为几个较短的DFT,可使乘法次数大大减少。另外,旋转因子WmN具有周期性和对称性。
    (考虑x(n)为复数序列的一般情况,对某一个k值,直接按上式计算X(k)值需要N次复数乘法、(N-1)次复数加法.)
    方法:
    分解N为较小值:把序列分解为几个较短的序列,分别计算其DFT值,可使乘法次数大大减少;
    利用旋转因子WNk的周期性、对称性进行合并、归类处理,以减少DFT的运算次数。
    周期性:这里写图片描述
    对称性:这里写图片描述这里写图片描述这里写图片描述
    3.FFT算法思想
    不断地把长序列的DFT分解成几个短序列的DFT,并利用旋转因子的周期性和对称性来减少DFT的运算次数。
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
    再次分解,对N=8点,可分解三次。
    这里写图片描述
    这里写图片描述
    c语言程序:

    // FFT.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include<stdio.h>
    #include<math.h>
    #include<stdlib.h>
    #include<windows.h>
    #define N 1000
    typedef struct{
        double real;
        double img;
     }complex;
    void fft();/*快速傅里叶变换*/
    void ifft();
    void initW();
    void change();
    void add(complex, complex, complex *);/*复数加法*/
    void mul(complex,complex,complex *);    /*复数乘法*/
    void sub(complex,complex,complex *);/*复数减法*/
    void divi(complex, complex,complex *);/*复数除法*/
    void output();   /*输出结果*/
    complex x[N], *W;/*输出序列的值*/
    int size_x = 0;/*输入序列的长度,只限2的N次方*/
    double PI;
    int main(){
        int i, method;
        system("cls");
        PI = atan(1.0) * 4;
        printf("Please input the size of x: \n");/*输入序列的长度*/
        scanf("%d", &size_x);
        printf("Please input the data in x[N](such as:5 6):\n");
        /*输入序列对应的值*/
        for (i = 0; i<size_x; i++)
              scanf("%lf %lf", &x[i].real, &x[i].img);
        initW();
        /*选择FFT或逆FFT运算*/
        printf("Use FFT(0) or IFFT(1) ?\n");
        scanf("%d", &method);
          if (method == 0)
              fft();
          else
              ifft();
          output();
          system("pause");
          return 0;
    }
        /*进行基-2 FFT运算*/
    void fft(){
          int i = 0, j = 0, k = 0, l = 0;
          complex up, down, product;
          change();
          for (i = 0; i < log((float)size_x) / log(2.0); i++)
          {
              l = 1 << i;
              for (j = 0; j<size_x; j += 2 * l)
              {
                  for (k = 0; k<l; k++)
                  {
                      mul(x[j + k + l], W[size_x*k / 2 / l], &product);
                      add(x[j + k], product, &up);
                      sub(x[j + k], product, &down);
                      x[j + k] = up;
                      x[j + k + l] = down;
                  }
              }
          }
      }
    void ifft()
    {
        int i = 0, j = 0, k = 0;
    
        complex up, down;
        for (i = 0; i < log((float)size_x) / log(2.0); i++)  /*蝶形运算*/
        {
            int l = size_x;
            l/= 2;
            for (j = 0; j < size_x; j += 2 * l)
            {
                for (k = 0; k < l; k++)
                {
                    add(x[j + k], x[j + k + l], &up);
                    up.real /= 2;
                    up.img /= 2;
                    sub(x[j + k], x[j + k + l], &down);
                    down.real /= 2;
                    down.img /= 2;
                    divi(down, W[size_x*k / 2 / l], &down);
                    x[j + k] = up;
                    x[j + k + l] = down;
                }
            }
        }
        change();
    }
    /*初始化变化核*/
    void   initW()
    {
        int i;
        W = (complex   *)malloc(sizeof(complex)*   size_x);
        for (i = 0; i < size_x; i++)
        {
            W[i].real = cos(2 * PI / size_x*i);
            W[i].img = -1 * sin(2 * PI / size_x*i);
        }
    }
        /*变址计算,将x(n)码位倒置*/
        void change()
        {
            complex temp;
            unsigned short i = 0, j = 0, k = 0;
            double t;
            for (i = 0; i<size_x; i++)
            {
                k = i;
                j = 0;
                t = (log((float)size_x) / log(2.0));
                while ((t--)>0)
                {
                    j = j << 1;
                    j |= (k & 1);
                    k = k >> 1;
                }
                if (j > i)
                {
                    temp = x[i];
                    x[i] = x[j];
                    x[j] = temp;
                }
            }
    }
    void output()/*输出结果*/
    {
            int i;
            printf("The result are as follows: \n");
            for (i = 0; i<size_x; i++)
            {
                printf("%.4f", x[i].real);
                if (x[i].img >= 0.0001)
                    printf("+%.4fi\n", x[i].img);
                else if(fabs(x[i].img)<0.0001)
                    printf("\n");
                else
                    printf("%.4fi\n", x[i].img);
            }
        }
    void add(complex a, complex b, complex *c)
    {
            c->real = a.real + b.real;
            c->img = a.img + b.img;
    }
    void mul(complex a, complex b, complex *c)
    {
            c->real = a.real*b.real- a.img*b.img;
            c->img = a.real*b.img+ a.img*b.real;
    }
    void sub(complex a, complex b, complex *c)
    {
            c->real = a.real - b.real;
            c->img = a.img - b.img;
    }
    void divi(complex a, complex b, complex *c)
    {
        c->real = (a.real*b.real + a.img*b.img) / (b.real*b.real + b.img*b.img);
        c->img = (a.img*b.real - a.real*b.img) / (b.real*b.real + b.img*b.img);
    }

    测试1
    测试2

    展开全文
  • 文章目录傅里叶变换及傅里叶逆变换定义窗函数/矩形脉冲信号的傅里叶变换基于MATLAB的快速傅里叶变换FFT 傅里叶变换及傅里叶逆变换定义 能从时域的非周期连续信号转化到频域非周期连续信号。 窗函数/矩形脉冲...

    傅里叶变换及傅里叶逆变换定义

    能从时域的非周期连续信号转化到频域非周期连续信号。
    在这里插入图片描述

    窗函数/矩形脉冲信号的傅里叶变换

    在这里插入图片描述
    在这里插入图片描述
    结论:

    1. 随着脉冲宽度τ的减小,主叶变得更宽,而且更多的能量被移到更高的频率。则反之。
    2. 当信号脉冲在时间上扩展时,它的变换在频率上压缩。则反之。

    基于MATLAB的快速傅里叶变换FFT

    通过蝶形算法衍生的FFT暂不推导,下面直接给出代码。

    % 功能:简易的快速傅里叶变换FFT
    % 注意:整周期采样的问题,非整周期采样会引起频谱泄露
    %       当NFFT/Fs*f1是整数时,称整周期采样
    
    % 编辑者:lily
    % 日期:2019,4,15
    
    clear;
    clc;
    close all;
    % ======================= input signal ==========================
    f1 = 30;
    fai1 = pi/3;
    Fs = 2^9;
    T =1;      % T = N/Fs 
    t = 0:1/Fs:T-1/Fs;
    N = length(t);
    NFFT = 2^nextpow2(N);%nextpow2(N),靠的最近的2的指数
    x = sin(2*pi*f1*t);
    % ======================= fft ==================================
    deltaF = 1/T;   %  deltaF = dFs/(N-1);%如果是表格或mat文件,推荐用这一种
    vecf = (0:N-1)*deltaF;
    
    % vecf  = linspace(0,Fs,N); 
    % linspace(x1,x2,n) % 生成 n 个点。这些点的间距为 (x2-x1)/(n-1)% 如果数据x是奇数,f=vecf
    
    tic;
    xk = 2*fft(x,NFFT)/N;        
    toc;
    
    Ampli = abs(xk);
    phase = unwrap(angle(xk));
    % =========================== figure ====================================
    figure;
    subplot(3,1,1);plot(t,x);title('信号')
    subplot(3,1,2);plot(vecf,Ampli);title('FFT双边谱')
    subplot(3,1,3);plot(vecf(1:Fs/2+1),Ampli(1:Fs/2+1));title('FFT单边谱')
    
    
    
    展开全文
  • 然后对音频号进行快速傅里叶变换fft(y,N),N取32768,画出信号的频谱特性,加深对频谱特性的理解。(3)根据频谱,反演时域特性,画出时域波形。寻找幅值最大的两个频率,此频率除以fft点数在乘以采样频率就是信号的...
  • 学习DIP第7天,图像傅里叶变换 转载请标明出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客...。。。。。。。...http://www.face2ai.com/DIP-2-5-图像傅里叶变换-快速傅里叶变换FFT/ http://www.to...

       学习DIP第7天,图像傅里叶变换

    转载请标明出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不满意。。。。。。。。

     

    内容迁移至  

    http://www.face2ai.com/DIP-2-5-图像傅里叶变换-快速傅里叶变换FFT/

    http://www.tony4ai.com/DIP-2-5-图像傅里叶变换-快速傅里叶变换FFT/

     

    展开全文
  • C#快速傅里叶变换FFT

    热门讨论 2013-08-19 00:42:04
    C#源代码,快速傅里叶变换FFT,计算结果和Matlab相同。
  • #本文讨论了一种可在#$%& 上实现的##’ 结构’ 该结构 采用基于流水线结构和快速并行乘法器的蝶形处理器’ 乘法器 采用改进的())*+ 算法$简化...关键词#快速傅立叶变换( 数字信号处理( 专用集成电路( 现场 可编程门阵列
  • 详细地介绍了快速傅里叶变换卷积的原理,在DSP应用有着经久不衰的历史。FFT和采样原理
  • 快速傅里叶变换FFT算法及其应用 把一维 二维傅里叶变换通过程序实现
  • 快速傅里叶变换FFT.c

    2020-07-01 10:55:31
    快速傅里叶算法,C语言,C语言源代码程序,详细的计算过程,适合初学者参考, 每一个函数都有定义。FFT中有一个WN的n次方项,在迭代中会不断用到,具体见算法说明。
  • 快速傅里叶变换的C语言实现 给出了封装好的接口 方便直接调用。
  • vb平台实现简单的FFT傅里叶变换算法,算法简单可行
  • 一维快速傅里叶变换FFT的C++实现,里面是FFT1.cpp函数,用于进行一维数组的FFT。有详细的注释和说明。
  • FFT的VHDL程序,quartus开发,有仿真数据和波形,下载到电路板里实测通过
  • 没有调用matlab自带的fft函数,而是自己编写的二维快速傅里叶变换fft程序 matlab平台 没有调用matlab自带的fft函数,而是自己编写的二维快速傅里叶变换fft程序 matlab平台
  • 快速傅里叶变换FFT进行频谱分析(matlab)

    万次阅读 多人点赞 2019-07-04 16:03:09
    本章摘要:FFT是离散傅立叶变换的快速算法,可以将一个信号变换到频域。有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易...本章主要讲解如何采用matlab进行傅里叶变换,以及需要注意的事项。
  • 快速傅里叶变换FFT总结

    千次阅读 2017-08-08 22:08:51
    快速傅里叶变换,在竞赛中离散傅里叶变换DFT及其逆变换IDFT尤为常用,主要用于快速求多项式的乘积。
  • 基于python的快速傅里叶变换FFT(一)

    万次阅读 多人点赞 2018-03-31 16:20:48
    基于python的快速傅里叶变换FFT(一) FFT可以将一个信号变换到频域。有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了。这就是很多信号分析采用FFT变换的原因。另外,FFT可以将...
  • MATLAB中的快速傅里叶变换FFT与IFFT

    万次阅读 多人点赞 2018-04-18 22:18:13
    FFT (Fast Fourier Transform)是离散傅立叶变换快速算法,可以将一个信号从时域变换到频域。同时与之对应的是IFFT(Inverse Fast Fourier Transform)离散傅立叶反变换快速算法。为掌握FFT和IFFT在MATLAB中的...
  • 快速傅里叶变换 FFT 板子

    千次阅读 2016-08-29 15:18:22
    参考了一些大神的板子后加上理解,自己写的一个板子 题目:UOJ #34 多项式乘法
  • 通俗点来说,FFT就是利用某些奇偶特点,进行DFT(离散傅里叶变换)和IDFT(离散傅里叶逆变换)的快速求解算法。 (2)FFT是干什么的,有什么用 <1>在信号学中有很大用处(具体什么用俺也不知道) <2>...
  • GSL快速傅里叶变换FFT

    千次阅读 2013-08-15 14:27:25
    #include #include #include #include #pragma comment(lib, "libgsl_d.lib") #pragma comment(lib, "libgslcblas_d.lib") #define REAL(z,i) ((z)[2*(i)]) #define IMAG(z,i) ((z)[2*(i)+1]) ...
  • 快速傅里叶变换:理解为实现DFT的快速算法,只是单纯的让数字信号处理器DSP跑DFT算法更快点。 傅里叶变换将信号转换到频域上去分析,学术研究可以用连续信号,模拟域的傅里叶变换去分析问题,但是计算机是无法分析...
  • 一个简单的C99快速傅里叶变换 (FFT)
  • 简要记录FFT实现原理

空空如也

1 2 3 4 5 ... 20
收藏数 14,528
精华内容 5,811
关键字:

快速傅里叶变换