フーリェ変換(特に一次元のFFT)とは?

このQ&Aのポイント
  • フーリェ変換について勉強しているものです。このページで配布している、fft.tgzというファイルのdfst()という関数を使おうと思っているんですが、その関数の返り値(返り配列と言った正しい?)の詳しい意味がよくわからないのです。
  • フーリエ変換は、信号や波形を周波数スペクトルに変換する手法です。一次元のFFT(高速フーリエ変換)は、離散的な信号を高速にフーリエ変換するアルゴリズムです。
  • このページにはfft.tgzというファイルがあり、その中にdfst()という関数が含まれています。dfst()はフーリエ変換の一種であり、sin波をまとめた配列を返す役割を持っています。具体的には、式の分割によって2つの配列が生成されます。
回答を見る
  • ベストアンサー

フーリェ変換(特に一次元のFFT)

フーリェ変換について勉強しているものです。このページ(http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/ftmndl.html)で配布している、fft.tgz (71 KB)というファイルのdfst()という関数を使おうと思っているんですが、その関数の返り値(返り配列と言った正しい?)の詳しい意味がよくわからないのです。 --------------------------------------------------------- 以下の変換は : S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n このように分割される : S[2*k] = sum_j=1^n/2-1 (a[j]-a[n-j])*sin(pi*j*k/(n/2)), ◎ S[2*k+1] = sum'_j=1^n/2 (a[j]+a[n-j])*sin(pi*j*(k+1/2)/(n/2)) 注意:sum_■^▲はΣを表しており、■から▲までの部分和となっています。 nは要素数です。 ------------------------------------------------------ この◎の関数はどのような意味を持つのでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • stomachman
  • ベストアンサー率57% (1014/1775)
回答No.1

実務でお困りなのだろうと推測します。 [1]dfst()なる関数がどこにあるんだか、また、ご質問の式がどこから出て来たものだか分かりませんけれども、周期2nを持つ実奇関数a[j]の離散Fourier変換(しかも、nが偶数の場合)の計算方法の話ですね。 > この◎の関数はどのような意味を持つのでしょうか? に直裁に回答するなら、「関数aの離散フーリエ変換の、sine成分のうち、2k+1番目の成分の振幅」ですが、ただしご質問の◎の式は(a[n/2]=0である場合を除いて)間違ってます。 [2]a[j]が周期2nを持つということは a[j+2*n]=a[j] という意味であり、a[j]が奇関数であるとは a[-j]=-a[j] を満たすということです。従って、さらに a[j]=-a[2*n-j] a[n-j]=-a[n+j] も成り立つことが分かります。 [3]周期2nを持つ複素関数a[j]の離散Fourier変換をF[k]とすると、0≦k<2nについて F[k] = c[k] + i*s[k] (iは虚数単位) c[k] = sum_j=0^2*n-1 a[j]*cos(2*pi*j*k/(2*n)) s[k] = sum_j=0^2*n-1 a[j]*sin(2*pi*j*k/(2*n)) です。  これをa[j]が奇関数である場合に限定すると、計算するまでもなく c[k]=0, 0≦k<2*n である。  一方、a[j]が実関数である場合に限定すると、 c[2*n-k]=c[k] s[2*n-k]=-s[k] である。  従って、a[j]が実奇関数である場合に限定すると、 c[k]=0 s[2*n-k]=-s[k] である。さらに s[0]=0 である。だから0<k<nについてs[k]を計算するだけでF[k]が分かることになります。 [4]そこでs[k]の計算を、a[j]の性質(s[2*n-k]=-s[k])を使って簡単にします。 s[k] = sum_j=0^2*n-1 a[j]*sin(pi*j*k/n) において、j=0のときとj=nのときにはsin(pi*j*k/n)=0なので、 s[k] = {sum_j=1^n-1 a[j]*sin(pi*j*k/n)}+{sum_j=n+1^2*n-1 a[j]*sin(pi*j*k/n)} 右辺の第二項を s[2*n-k]=-s[k] を使って書き換えると、 第二項 = sum_j=1^n-1 a[j]*sin(pi*j*k/n) ところがこれは第一項とピッタリ同じなので、結局 s[k] = 2*{sum_j=1^n-1 a[j]*sin(pi*j*k/n)} です。だから、ご質問の最初の式 S[k]=sum_j=1^n-1 a[j]*sin(pi*j*k/n) は、s[k]の1/2倍である。 [5]ようやく本題です。nが偶数であるという条件を使って、S[k]の計算をさらに簡単にしようと試みます。 [5-1] kが偶数(k=2m)の場合、 j=n/2のとき sin(2*pi*(n/2)*m/n)=sin(pi*m)= 0 だから S[2m]={sum_j=1^n/2-1 a[j]*sin(2*pi*j*m/n)}+{sum_j=n/2+1^n-1 a[j]*sin(2*pi*j*m/n)} です。右辺の第二項は -sum_j=1^n/2-1 a[n-j]*sin(2*pi*j*m/n) と書き換える事ができ、従って S[2*m] = sum_j=1^n/2-1 (a[j]-a[n-j])*sin(2*pi*j*m/n) [5-2] kが奇数(k=2m+1)の場合、 S[2*m+1] = sum_j=1^n-1 a[j]*sin(pi*j*(2*m+1)/n) = {sum_j=1^n/2-1 a[j]*sin(pi*j*(2*m+1)/n)}+a[n/2]*sin(pi*(n/2)*(2*m+1)/n)+{sum_j=n/2+1^n-1 a[j]*sin(pi*j*(2*m+1)/n)} 右辺の第三項は sum_j=1^n/2-1 a[n-j]*sin(pi*j*(2*m+1)/n) と書き換えることができ、また第二項は a[n/2]*sin(pi*(n/2)*(2*m+1)/n)=a[n/2]*sin((m+1/2)*pi)= mが偶数→a[n/2]、mが奇数→-a[n/2] です。以上から、 S[2*m+1] = {sum_j=1^n/2-1 (a[j]+a[n-j])*sin(pi*j*(2*m+1)/n)}+a[n/2]*sin((m+1/2)*pi) あるいは、 S[2*m+1] = {sum_j=1^n/2 (a[j]+a[n-j])*sin(pi*j*(2*m+1)/n)}-a[n/2]*sin((m+1/2)*pi) と書いても同じです。  だから、a[n/2]≠0のとき、◎の式は間違っています。 [6] この間違いを修正するには、 p[j]=a[j]-a[n-j] (0<j<n/2) q[j]=a[j]+a[n-j] (0<j<n/2) q[n/2]=a[n/2] として、 S[2*m] = sum_j=1^n/2-1 p[j]*sin(2*pi*j*m/n) S[2*m+1] = sum_j=1^n/2 q[j]*sin(pi*j*(2*m+1)/n) で計算すれば良い。そもそも、p, qを作っておかないと(つまりmが変わるごとに毎回a[j]+a[n-j]やa[j]-a[n-j]を計算していると)、[5]の変形による計算量の削減効果が発揮できません。

0-o
質問者

お礼

返答ありがとうございます。非常に丁寧に説明してくださいまして、とても感激しました。まだ完全に理解したわけではないので、質問を締め切ったのち、じっくり読ませていただきます。

関連するQ&A

  • 一次元のフーリエ変換(FFT)

    フーリェ変換について勉強しているものです。このページ(​http://www.kurims.kyoto-u.ac.jp/~ooura/fftman/ftmndl.html)で配布している、fft.tgz (71 KB)というファイルのdfst()という関数を使おうと思っているんですが、その関数の返り値(返り配列と言った方が正しい?)の詳しい意味がよくわからないのです。 以下はこの関数のマニュアル(readme.txt)の一部です。 dfst()の大まかな処理内容を示しています。 --------------------------------------------------------- 以下の変換は : S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n このように分割される : S[2*k] = sum_j=1^n/2-1 (a[j]-a[n-j])*sin(pi*j*k/(n/2)), ◎ S[2*k+1] = sum'_j=1^n/2 (a[j]+a[n-j])*sin(pi*j*(k+1/2)/(n/2)) 注意:sum_■^▲はΣを表しており、■から▲までの部分和となっています。 nは要素数です。 ------------------------------------------------------ この◎の配列はどのような意味を持つのでしょうか?

  • FFTをc言語でプログラミング

    助けてください↓c言語でFFTをプログラミングしているのですがバグが発見できずに困り果て居ます。 プログラミングは以下の通りです。画像はカラーではなく、グレースケールのMANDRILLに対して行っています。 for(n=0;n<M;n++){    for(i=0;i<N;i++){ for(j=0;j<N;j++){ Imbatx[n][i][j]=0; } } } //行FFT //まずはソート for(i=0;i<N;i++){ int x=1; for(j=0;j<N;j++){ if(j<N/2){        //左半分 if(j%2!=0) //jが奇数 batx[0][i][j]=imagein[i][j+N/2-1]; if(j%2==0) //jが偶数 batx[0][i][j]=imagein[i][j]; } if(N/2<=j && j<N){ //右半分 if(j%2!=0) //jが奇数 batx[0][i][j]=imagein[i][x+N/2-1];      if(j%2==0) //jが偶数 batx[0][i][j]=imagein[i][x]; x++; } } } for(i=0;i<N;i++){ for(n=1;n<M+1;n++){ k=0; for(j=0;j<N;j++){ mul=(int)pow(2.0,(double)n); if(j%mul < mul/2){ //偶数列 batx[n][i][j] = batx[n-1][i][j]+cos(2*PI*k/mul)*batx[n-1][i][j+mul/2]- sin(2*PI*k/mul)*Imbatx[n-1][i][j+mul/2]; Imbatx[n][i][j]=Imbatx[n-1][i][j]+sin(2*PI*k/mul)*batx[n-1][i][j+mul/2]+ cos(2*PI*k/mul)*Imbatx[n-1][i][j+mul/2]; k++; } else{ //奇数列 batx[n][i][j] = cos(2*PI*k/mul)*batx[n-1][i][j] + batx[n-1][i][j-mul/2]- sin(2*PI*k/mul)*Imbatx[n-1][i][j]; Imbatx[n][i][j]=Imbatx[n-1][i][j-mul/2]+sin(2*PI*k/mul)*batx[n-1][i][j]+cos(2*PI*k/mul)*Imbatx[n-1][i][j]; k++; } Re_countx[i][j] = batx[M][i][j]; Im_countx[i][j] = Imbatx[M][i][j]; } } } for(i=0;i<N;i++){ for(j=0;j<N;j++){ FFT[i][j]=pow(pow(Re_countx[i][j],2)+pow(Im_countx[i][j],2),0.5); FFT[i][j]=log10(FFT[i][j]); if(FFT[i][j]>max) max=FFT[i][j]; imageout[i][j]=(unsigned char)255*FFT[i][j]/max; } } 結果的に出力される画像は以下の画像になります。 ごらんのように縞模様が出てしまいます。 分かる方どうかお願いいたします。

  • Cプログラムによる画像の高速フーリエ変換FFT

    Cプログラムにる画像処理について教えてください。逆高速フーリエ変換(IFFT)によって周波数領域から実空間領域に変換する際、周波数帯域を(低周波数領域に)制限してIFFTを実行できるという文献があるのですが可能でしょうか?通常の逆離散フーリエ変換(IDFT)では可能でした。 以下のコードの修正で可能でしょうか? 例) 512*512の空間周波数領域データを212*212(仮に)の低周波数領域に帯域制限してIFFTで画像に戻したいのです。 以下の以下の一次元FFTコードを用いてX方向とY方向にIFFTしたいと考えています。 void FFT(int ir, int nx, float *xr, float *xi, float *si, float *co, unsigned short *brv) // 1次元フーリエ変換 // int ir; 順変換(1)と逆変換(-1) // int nx; 1次元FFTのデータ数 // float *xr; 実部のデータ xr[nx] // float *xi; 虚部のデータ xi[nx] // float *si; FFT用のサインデータ si[nx/2] // float *co; FFT用のコサインデータ co[nx/2] // unsigned short *brv; FFT用の入れ替えデータ brv[nx] { int i, j, n1, n2=nx, j3, j4, k, l, ll, d=1, g; float a, b, c, s; for(l = 1; l <= nx/2; l *= 2, d += d) { g = 0; ll = n2; n2 /= 2; for(k = 1; k <= n2; k++) { n1 = k-ll; c = co[g]; s = -ir*si[g]; g += d; for(j = ll; j <= nx; j += ll) { j3 = j+n1-1; j4 = j3+n2; a = xr[j3]-xr[j4]; b = xi[j3]-xi[j4]; xr[j3] += xr[j4]; xi[j3] += xi[j4]; xr[j4] = c*a+s*b; xi[j4] = c*b-s*a; } } } 通常のDFTでの帯域制限はこのように行い可能でした。 void fourier1d(int ir, float *fr, float *fi, int nx) { int i, j, n = 1; float *gr, *gi; double u, x; gr = (float *)malloc((unsigned long)nx*sizeof(float)); gi = (float *)malloc((unsigned long)nx*sizeof(float)); for(i = 0 ; i < nx ; i++) { u = i-nx/2; gr[i] = gi[i] = 0; for(j = 150 ; j < 362 ; j++) { x = j-nx/2; gr[i] += (float)( fr[j]*cos(2*PI*u*x/nx)+ir*fi[j]*sin(2*PI*u*x/nx)); gi[i] += (float)(-ir*fr[j]*sin(2*PI*u*x/nx)+fi[j]*cos(2*PI*u*x/nx)); } } if(ir == -1) n = 212; // 逆変換はデータ数で割る for(i = 0 ; i < nx ; i++) { fr[i] = gr[i]/n; fi[i] = gi[i]/n; } free(gr); free(gi); }

  • 二次元空間におけるフーリエサイン変換のプログラミング(C)について

    二次元のフーリエサイン変換を考えています。 正方形の中心にだけ高い数値を持つ状況をつくり、その波数分布を示したい(最終目標は違いますが・・・)のですがうまくいきません。 下記のように書いてみましたが、コンパイルは通っても、変換した値が一様に0となってしまいます。 よろしければ、見てやってください。 #include<stdio.h> #include<math.h> #define max 100 #define pi 3.141592 main() { //変数i,jが位置、ki,kjが波数に当たります int i,j,ki,kj; //Nはデータ点の数 int N=30; double a[max][max]; double A[max][max]; //aの分布をデルタ関数的なものとします。 for(i=0;i<N;i++){ for(j=0;j<N;j++){ a[i][j]=0; }} a[N/2+1][N/2+1]=100; for(ki=0;ki<N;ki++){ for(kj=0;kj<N;kj++){A[ki][kj]=0.0; for(i=0;i<N;i++){ for(j=0;j<N;j++){ A[ki][kj] += (2/N)*(2/N)*a[i][j]*sin((2*pi*ki*i)/N)*sin((2*pi*kj*j)/N); }}}} return(0); }

  • 離散フーリエ変換について

    G(n/Nτ)= Σ[k=0->N-1] {τ*f(kτ)*e^(-i2πkn/N)} 上記の式は離散フーリエ変換の式らしいですが、 これを関数化しようとしてつまずいています。 どのように解釈すれば、関数化できるのでしょうか? 特に、複素数iがよくわかりません。 e^iが点を左に90度回転させるくらいはわかります。 e^(-i2πkn/N)を関数powで表現できなくて困っていますが、多分見当違いだとは思います。 フーリエ級数展開はわかります。 最終的にはFFTを行いたいのですが、 その理解の前に離散フーリエ変換ができないといけないと思っています。 よろしくお願いします。 void GraphClass::ScatteredFourierConvert() { /* N-1 G(n/Nτ)= Σ {τ*f(kτ)*e^(-i2πkn/N)} k=0 k=何番目の値か? τ=値を読んだ間隔 N=値を読んだ数 */ int Tau=10; int N=RangeSize.cx*2/Tau; double Sum=0; double e=2.7182818; for(int k=0;k<N-1;k++) { Sum+=Tau*Data[k*Tau]*pow(e,-i*2*PI*k*n/N); } }

  • EXCELを使ってFFT(高速フ-リエ変換)をする方法2

    添付ファイルの波形のスペクトル及びパワースペクトルを求め、それらの違いについて考察せよ。ただし、データ数1024個でFFTを行うこと。 1.波形1:sin波 1周期(振幅1) A:1(A1)-1024(A1024)の番号が並んでいる B:B1=SIN(2*PI()*(A1-1)/1024+PI()/4)~B1024=SIN(2*PI()*(A1024-1)/1024+PI()/4) この場合EXCELでどのような操作を行うとFFT(高速フ-リエ変換)をした波形になるのでしょうか?結果は複素数になるのでグラフ化のとき何かしなければならないと思うのですが詳しい方教えてください.お願いします. 波形のスペクトルとパワースペククトルの違いもよくわかりません。

  • C言語でのFFTについて

    http://tsuyu.cocolog-nifty.com/blog/2007/03/publi.html に掲載されているVBAのFFTプログラムをC言語に書き換えて 実行しているのですがうまくいきません。 どこが、間違っているか教えてください。 ======以下FFTのサブルーチンソースコード===== void FFT(float Xr[], float Xi[]) { i=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0; n=DN; m = log10(n)/log10(2); Table(c,s); l=n,h=1; for(g=1;g<=m;g++){ l/=2,k=0; for(q=1;q<=h;q++){ p=0; for(i=k;i<=l+k-1;i++){ j=i+l; a=Xr[i]-Xr[j], b=Xi[i]-Xi[j]; Xr[i] = Xr[i] + Xr[j], Xi[i] = Xi[i] + Xi[j]; if(p==0){ Xr[j]=a,Xi[j]=b; }else{ Xr[j] = a * c[p] + b * s[p], Xi[j] = b * c[p] - a * s[p]; } p+=h; } k+=l+l; } h+=h; } j=n/2; for(i=1;i<=n-1;i++){ k=n; if(j<i){ //ビットリバース swap(&Xr[i],&Xr[j]); swap(&Xi[i],&Xi[j]); } k=k/2; while(j>=k){ j=j-k; k /=2; } j = j + k; } }

  • FFT(高速フーリエ変換)の考え方

    ///////////質問(1)//////////////////// 離散フーリエ変換が以下の式で表せることは理解できます。      N-1       nk          -2πi / N A =  Σ  a   W ,   W = e    n   k=0   k                 n:基本周波数のn倍 N:データ数 ここで、実際のプログラム上では、 (1)akに サンプリングしたデータとサンプリング周期を掛けたもの を入れ、 (2)それをWと掛けあわせて行くと思います。 では、Wには何を代入すればいいのでしょうか? Wを実部・虚部にわける、もしくは、 絶対値・位相にわけて計算する方法で あっているのでしょうか? Anは周波数n成分の面積を表していると思うのですが、面積に虚数概念がで てくるのも変な話なような気がして、質問させていただきました。 ///////////質問(2)////////////////////    N/2-1    2nk   n N/2-1     2nk A = Σ a  W + W  Σ  a  W  n  k=0  2k        k=0  2k+1 Cooley-Tukey 型 FFTは質問(1)の式を上記のように2項に分解することで、 計算を減らすことができるとのことです。 しかし、私には理解できません。 左側の項でN/2回の掛け算を行い、右側の項でもN/2回の掛け算を行うのでは、 結局式を分解しただけで、何も変わっていないように思えます。 どのように考えればいいのでしょうか? アドバイスよろしくお願いします。

  • Octaveを使ったFFTについて

    Windows用のGNU Octave3.0.3を使っているのですが、 今回OctaveのFFT関数を試してみようと思い 「​http://diaspar.jp/node/209​」 ↑このサイトに書いてある通りのプログラムで、 簡単な合成波にFFTをしてみました。 結果、サイトに書いてある通りのグラフが表示されたのですが、 元の式がy=sin(2*pi*100*t) + sin(2*pi*150*t)ですので、 100Hzと150Hzでピークを迎えることはわかるのですが、 なぜピーク時での値が約90になるのでしょうか? (私は同じピークを迎えるとしても、90ではなく値が1になると思っていました。) この結果はいったい何を表わしているのでしょうか? よろしくお願いいたします。

  • MDCT→FFTへの変換?(オーディオ信号)

    いまオーディオ圧縮アルゴリズムの勉強をしています。 IMDCTの式が    y[n]= (2/N)*Σ{x[k]*cos((2π/N)*(n+a)*(k+0.5))}         k ただし0≦n<N, aは定数, Σはk=0,1,…N/2-1 とあるのですが、これをFFTで計算するには どのような処理をすればいいのでしょうか? また、cos(x)の計算は何回すればいいのでしょうか? #cos(x)の値をテーブルで持っておくには、 #何種類の値をもっていればいいのでしょうか? 本やネットで調べたら、DCTの式はあるのですが、 MDCTについてはあまり触れられていないので… よろしくお願いします。