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

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

  • のこぎり波を生成するプログラム例
    import java.io.*;
    
    class Sawwave
    {
            public static void main(String arg[])
            {
                    int i,n,m,max;
                    n=0;
                    max=100;        //最大繰り返し回数
                    double x,y[]=new double[max];
    
                    //のこぎり波の一周期分の数値をつくる。
                    for(x=-10;x<=10;x+=1) 
                    {
                            y[n]=x;
                            n++;
                    }
    
                    try
                    {
                            FileWriter fw=new FileWriter("function.data");
    
                            //のこぎり波の生成
                            for(i=0;i<max;i++) 
                            {
                                    for(m=0;m<n;m++) 
                                    {
                                            fw.write(y[m] + "\n");
                                    }
                            }
                            fw.close();
                    }
                    catch(Exception e)
                    {
                            System.out.println(e);
                    }
            }
    }
    

    これを GNUPLOT で出力すると、

  • フーリエ変換のプログラム例
    import java.io.*;
    
    class Fourier
    {
            public static void main(String arg[])
            {
                    int k,n,N,max;
                    N=0;
                    max=10000;
                    double f[]=new double[max+1],ReF,ImF;
    
                    try
                    {
    //フーリエ変換したいデータをあらかじめファイル function.data に保存しておく
                            FileReader fr=new FileReader("function.data");
                            StreamTokenizer st=new StreamTokenizer(fr);
    //データの読み込み(ただし実数の成分のみ)
                            while(st.nextToken()!=StreamTokenizer.TT_EOF)
                            {
                                    if(N>max)
                                            break;
                                    f[N]=st.nval;
                                    N++;
                            }
    
                            FileWriter fw=new FileWriter("fourier.data");
    
                            //実数部分と虚数部分に分けてフーリエ変換
                            for(n=0;n<N;n++) 
                            {
                                    ReF=ImF=0.0;
                                    for(k=0;k<N;k++) 
                                    {
                                            ReF+=f[k]*Math.cos(2*Math.PI*k*n/(N+1));
                                            ImF+=-f[k]*Math.sin(2*Math.PI*k*n/(N+1));
                                    }
                                    fw.write(n + " " + ReF + " " + ImF + "\n");
                            }
                            fr.close();
                            fw.close();
                    }
                    catch(Exception e)
                    {
                            System.out.println(e);
                    }
            }
    }
    

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

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

  • 離散的逆フーリエ変換のプログラム例
    import java.io.*;
    import java.util.*;
    
    class Reversefourier
    {
            public static void main(String arg[])
            {
                    int k,n,N,max;
                    N=0;
                    max=10000;      //配列の個数(データ数に応じて余裕を持たせておく)
                    String s="";
                    double Ref,Imf;
                    double ReF[]=new double[max+1],ImF[]=new double[max+1];
    
                    try
                    {
    //逆フーリエ変換したいデータをあらかじめfourier.dataに保存しておく
                            FileReader fr=new FileReader("fourier.data");
                            BufferedReader br=new BufferedReader(fr);
                            FileWriter fw=new FileWriter("inversef.data");
    
                            while((s=br.readLine())!=null)
                            {
                                    StringTokenizer st=new StringTokenizer(s," ");
    
                                    N=Integer.parseInt(st.nextToken());
    
                                    ReF[N]=Double.parseDouble(st.nextToken());
    
                                    ImF[N]=Double.parseDouble(st.nextToken());
                            }
    
                            //フーリエ逆変換
                            for(k=0;k<N;k++) 
                            {
                                    Ref=Imf=0.0;
                                    for(n=0;n<N;n++) 
                                    {
                                            Ref+=(ReF[n]*Math.cos(2*Math.PI*k*n/N)-ImF[n]*Math.sin(2*Math.PI*k*n/N))/N;
                                            Imf+=(ReF[n]*Math.sin(2*Math.PI*k*n/N)+ReF[n]*Math.sin(2*Math.PI*k*n/N))/N;
                                    }
                                    fw.write(k + " " + Ref + " " + Imf + "\n");
                            }
    
                            fr.close();
                            fw.close();
                    }
                    catch(Exception e)
                    {
                            System.out.println(e);
                    }
            }
    }
    

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

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