みその計算物理学
ホーム はじめに リンク集
離散的フーリエ変換の数値計算例(C言語)

ここでは実際に離散的フーリエ変換を、簡単なのこぎり波に対して行ってみる。

  • のこぎり波を生成するプログラム例
    #include <stdio.h>
    #include <math.h>
    
    #define max 100         //最大繰り返し回数
    
    int main()
    {
            int i,n,m;
            double x,y[max];
            
            n=0;
    
            FILE *function;
            function=fopen("function.data","w");
    
    //のこぎり波の一周期分の数値をつくる。
            for(x=-1.0;x<=1.0;x+=0.1) {
                    y[n]=x;
                    n++;
            }
    
    //のこぎり波の生成
            for(i=0;i<max;i++) {
                    for(m=0;m<n;m++) {
                            fprintf(function,"%f\n",y[m]);
                    }
            }
    
            fclose(function);
    
            return 0;
    }
    

    これを GNUPLOT で出力すると、

  • フーリエ変換のプログラム例
    #include <stdio.h>
    #include <math.h>
    
    #define max 10000                       //Nの限界値
    #define pi 3.1415926535         //円周率
    
    int main()
    {
            int k,n,N;
            double f[max+1],ReF,ImF;
    
            FILE *function;
            FILE *fourier;
    //フーリエ変換したいデータをあらかじめファイル function.data に保存しておく
            function=fopen("function.data","r");
            fourier=fopen("fourier.data","w");
    
    //データの読み込み(ただし実数の成分のみ)
            for(N=0;N<max;N++) {
                    if(fscanf(function,"%lf",&f[N]) == EOF) {
                            N--;
                            break;
                    }
            }
    
    //実数部分と虚数部分に分けてフーリエ変換
            for(n=0;n<N;n++) {
                    ReF=ImF=0.0;
                    for(k=0;k<N;k++) {
                            ReF+=f[k]*cos(2*pi*k*n/N);
                            ImF+=-f[k]*sin(2*pi*k*n/N);
                    }
                    fprintf(fourier,"%d %f %f\n",n,ReF,ImF);
            }
    
            fclose(function);
            fclose(fourier);
            
            return 0;
    }
    

    GNUPLOTで出力結果を見てみる。上の画像が実数成分、下の画像が虚数成分である。

    横軸が周波数を表し、縦軸がその周波数の強さ(スペクトル)となる。

  • 離散的逆フーリエ変換のプログラム例
    #include <stdio.h>
    #include <math.h>
    
    #define max 10000                       //Nの最大限度数
    #define pi 3.1415926535         //円周率
    
    int main()
    {
            int i,k,n,N;
            double Ref,Imf;
            double ReF[max+1],ImF[max+1];
            FILE *fourier;
            FILE *inversef;
    
    //逆フーリエ変換したいデータをあらかじめfourier.dataに保存しておく
            fourier=fopen("fourier.data","r");
            inversef=fopen("inversef.data","w");
    
    //データの読み込み。
            for(N=0;N<max;N++) {
                    if(fscanf(fourier,"%d %lf %lf",&i,&ReF[N],&ImF[N]) == EOF) {
                            N--;
                            break;
                    }
            }
    
    //フーリエ逆変換
            for(k=0;k<N;k++) {
                    Ref=Imf=0.0;
                    for(n=0;n<N;n++) {
                            Ref+=(ReF[n]*cos(2*pi*k*n/N)-ImF[n]*sin(2*pi*k*n/N))/N;
                            Imf+=(ReF[n]*sin(2*pi*k*n/N)+ReF[n]*sin(2*pi*k*n/N))/N;
                    }
                    fprintf(inversef,"%d %f %f\n",k,Ref,Imf);
            }
    
            fclose(fourier);
            fclose(inversef);
    
            return 0;
    }
    

    GNUPLOTで出力してみる。理論的には元ののこぎり波に戻るはずである。

    やや違うところがあるがほぼ元ののこぎり波に戻った。しかし、もっと周波数が大きい部分では大きなずれが見られる。これは近似を行ったから誤差の発生はまぬがれないせいもあるが、比較的近似を粗く行ったせいもある。