高速フーリエ変換(FFT)ライブラリの紹介
2次元高速フーリエ変換(FFT)を実行するライブラリと利用方法を解説します。 本項では、ガウス関数と呼ばれる次の関数のフーリエ変換による展開係数の計算と、展開係数から元の関数へ逆変換を実行する方法を解説します。
手順1:関数の準備
関数fと項数Nと実空間の区間Lを用意します。
//項数と空間距離 var N = Math.pow(2, 7); //128 var L = 10; //元の関数 function f( x, y ){ var sigma = L/10; x = x - L/2; y = y - L/2; return Math.exp( -(x*x+y*y)/(sigma*sigma) ); }
手順2:高速フーリエ変換関数を実行
FFT2D_f関数の第一引数と第二引数に、フーリエ変換対象関数の実数部と虚数部、第三引数に区間の長さ、第四引数に項数(空間分割数)を与えます。 戻り値のresults.anとresults.bnに展開係数の実数部と虚数部が格納されます。結果を取得後グラフ描画するだけです。
//展開係数の計算 var results = FFT2D_f( f, null, L, N, false );
手順3:高速逆フーリエ変換関数を実行
IFFT2D関数の第一引数と第二引数に、展開係数の実部と虚部をそれぞれ2次元配列で渡します。 第三引数と第四引数に逆変換後の結果を受け取る2次元配列を渡します。
//展開係数の取得 var an = results.an; var bn = results.bn; //結果受取用の配列の準備 var fr = []; var fi = []; for( var i=0; i<N; i++ ){ fr[i] = []; fi[i] = []; } //展開係数の計算 IFFT2D( an, bn, fr, fi );
2次元フーリエ変換の結果
実際に計算した結果をグラフ描画(グラフ描画方法)します。
高速フーリエ変換(FFT)ライブラリ
1次元高速フーリエ変換
function FFT( an, bn, N, Inverse ){ ///////////////////////////////////////// //参考:Javaで学ぶシミュレーションの基礎 ///////////////////////////////////////// // 入力 // N : 項数(2のべき乗) // an : 実数配列(順変換:実数空間データを項数Nで指定、逆変換:展開係数a(n)) // bn : 実数配列(順変換:虚数空間データを項数Nで指定、逆変換:展開係数b(n)) // Inverse : 逆変換の場合に true ///////////////////////////////////////// // 出力 // an : 実数配列(順変換:展開係数a(n)、逆変換:実数空間データ) // bn : 実数配列(順変換:展開係数b(n)、逆変換:虚数空間データ) ///////////////////////////////////////// var ff = Inverse ? 1 : -1; var rot = new Array(N); for( var i = 0; i < rot.length; i++ ) rot[ i ] = 0; var nhalf = N/2, num = N/2; var sc = 2 * Math.PI / N; while( num >= 1 ){ for(var j = 0; j < N; j += 2 * num ){ var phi = rot[j] / 2; var phi0 = phi + nhalf; var c = Math.cos( sc * phi ); var s = Math.sin( sc * phi * ff ); for( var k = j; k < j + num; k++ ){ var k1 = k + num; var a0 = an[ k1 ] * c - bn[ k1 ] *s; var b0 = an[ k1 ] * s + bn[ k1 ] *c; an[ k1 ] = an[ k ] - a0; bn[ k1 ] = bn[ k ] - b0; an[ k ] = an[ k ] + a0; bn[ k ] = bn[ k ] + b0; rot[ k ] = phi; rot[ k1 ] = phi0; } } num = num / 2; } for( var i = 0; i < N ; i++ ){ var j = rot[ i ]; if( j > i ){ var tmp = an[ i ]; an[ i ] = an[ j ]; an[ j ] = tmp; var tmp = bn[ i ]; bn[ i ] = bn[ j ]; bn[ j ] = tmp; } } for( var i = 0; i < N ; i++ ){ an[ i ] = an[ i ] / Math.sqrt(N); bn[ i ] = bn[ i ] / Math.sqrt(N); } }
1次元高速フーリエ変換(関数形指定型)
function FFT_f( Fr, Fi, L, N ){ ///////////////////////////////////////// // 入力 // Fr : フーリエ変換を実行する関数の実部(function(x)) // Fi : フーリエ変換を実行する関数の虚部(function(x)) // L : 実空間の区間 0からL (float) // N : 項数(2のべき乗) (int) ///////////////////////////////////////// // 出力 return results; // results.an : フーリエ変換後の展開係数の実部 // results.bn : フーリエ変換後の展開係数の虚部 // results.replace.an : フーリエ変換後の展開係数の実部(n=0を配列要素の中心に) // results.replace.bn : フーリエ変換後の展開係数の虚部(n=0を配列要素の中心に) ///////////////////////////////////////// if( Fr == undefined ) Fr = function(){ return 0; }; if( Fi == undefined ) Fi = function(){ return 0; }; //展開係数 var fr = []; var fi = []; var an = []; var bn = []; for( var i=0; i<N; i++ ){ var x = L/N * i; fr[ i ] = Fr( x ); fi[ i ] = Fi( x ); } //フーリへ変換の実行 FFT( fr, fi, N, false); //結果を格納するオブジェクト var results = {}; results.an = []; results.bn = []; results.replace = {}; results.replace.an = []; results.replace.bn = []; for (var i = 0; i < N; i++) { results.an[ i ] = fr[ i ]; results.bn[ i ] = fi[ i ]; var ii; if( i < N/2 ) ii = N/2 + i; else ii = i - N/2; results.replace.an[ i ] = fr[ ii ]; results.replace.bn[ i ] = fi[ ii ]; } return results; }
2次元高速フーリエ変換(関数形指定型)
function FFT2D_f( Fr, Fi, L, N ){ ///////////////////////////////////////// // 入力 // Fr : フーリエ変換を実行する関数の実部(function(x,y)) // Fi : フーリエ変換を実行する関数の虚部(function(x,y)) // L : 実空間の区間 0からL (float) // N : 項数(2のべき乗) (int) ///////////////////////////////////////// // 出力 return results; // results.an : フーリエ変換後の展開係数の実部 // results.bn : フーリエ変換後の展開係数の虚部 // results.replace.an : フーリエ変換後の展開係数の実部(n=0を配列要素の中心に) // results.replace.bn : フーリエ変換後の展開係数の虚部(n=0を配列要素の中心に) ///////////////////////////////////////// if( Fr == undefined ) Fr = function(){ return 0; }; if( Fi == undefined ) Fi = function(){ return 0; }; if( Fr == undefined ) Fr = function(){ return 0; }; if( Fi == undefined ) Fi = function(){ return 0; }; //展開係数 var fr = []; var fi = []; var an = []; var bn = []; for( var i=0; i<N; i++ ){ fr[i] = []; fi[i] = []; an[i] = []; bn[i] = []; } for( var i=0; i<N; i++ ){ for( var j=0; j<N; j++ ){ var x = L/N * i; var y = L/N * j; fr[ i ][ j ] = Fr( x, y ); fi[ i ][ j ] = Fi( x, y ); } } //実空間格子上値から展開係数を計算 FFT2D( fr, fi, an, bn ); //結果 var results = {}; results.an = []; results.bn = []; results.replace = {}; results.replace.an = []; results.replace.bn = []; for (var i = 0; i < N; i++) { results.an[ i ] = []; results.bn[ i ] = []; results.replace.an[ i ] = []; results.replace.bn[ i ] = []; for (var j = 0; j < N; j++) { results.an[ i ][ j ] = an[ i ][ j ]; results.bn[ i ][ j ] = bn[ i ][ j ]; var ii = (i < N/2)? N/2 + i : i - N/2; var jj = (j < N/2)? N/2 + j : j - N/2; results.replace.an[ i ][ j ] = an[ ii ][ jj ]; results.replace.bn[ i ][ j ] = bn[ ii ][ jj ]; } } return results; }
2次元高速フーリエ変換(実空間格子上値から展開係数を計算)
function FFT2D( fr, fi, an, bn ){ ///////////////////////////////////////// // 入力 // fr : フーリエ変換を実行する関数の実部([][]) // fi : フーリエ変換を実行する関数の虚部([][]) ///////////////////////////////////////// // 出力 // an : フーリエ変換後の展開係数の実部([][]) // bn : フーリエ変換後の展開係数の虚部([][]) ///////////////////////////////////////// var N = fr.length; for( var i=0; i<N; i++ ){ //x軸に平行なFFTの実行 FFT( fr[ i ], fi[ i ], N ); } for( var i=0; i<N; i++ ){ for( var j=0; j<N; j++ ){ an[ i ][ j ] = fr[ j ][ i ]; bn[ i ][ j ] = fi[ j ][ i ]; } } for( var i=0; i<N; i++ ){ //y軸に平行なFFTの実行 FFT( an[ i ], bn[ i ], N); } }
2次元高速逆フーリエ変換(展開係数から実空間格子上値を計算)
function IFFT2D( an, bn, fr, fi ){ ///////////////////////////////////////// // 入力 // an : 逆フーリエ変換対象の展開係数の実部([][]) // bn : 逆フーリエ変換対象の展開係数の虚部([][]) ///////////////////////////////////////// // 出力 // fr : 逆フーリエ変換後の関数の実部([][]) // fi : 逆フーリエ変換後の関数の虚部([][]) ///////////////////////////////////////// var N = an.length; for( var i=0; i<N; i++ ){ //kxに平行なFFTの実行 FFT( an[ i ], bn[ i ], N, true ); } for( var i=0; i<N; i++ ){ for( var j=0; j<N; j++ ){ fr[ i ][ j ] = an[ j ][ i ]; fi[ i ][ j ] = bn[ j ][ i ]; } } for( var i=0; i<N; i++ ){ //ky軸に平行なFFTの実行 FFT( fr[ i ], fi[ i ], N, true); } }