2014-06-19 100 views
3

今天我寫了一個算法來計算表示離散函數的給定數組點的快速傅里葉變換。現在我試圖測試它,看它是否工作。我已經嘗試過十幾個不同的輸入集,而且他們似乎與我在網上找到的示例相匹配。然而,對於我的最終測試,我給出了cos(i/2)的輸入,其中i從0到31,並且根據我使用的求解器得到3個不同的結果。我的解決方案似乎是最不準確的:驗證FFT算法的正確性

Data

這是否表明我的算法有問題,或者是它只是相對較小的數據集的結果?

我的代碼如下,在情況下,它可以幫助:

/** 
* Slices the original array, starting with start, grabbing every stride elements. 
* For example, slice(A, 3, 4, 5) would return elements 3, 8, 13, and 18 from array A. 
* @param array  The array to be sliced 
* @param start  The starting index 
* @param newLength The length of the final array 
* @param stride The spacing between elements to be selected 
* @return   A sliced copy of the input array 
*/ 
public double[] slice(double[] array, int start, int newLength, int stride) { 
    double[] newArray = new double[newLength]; 
    int count = 0; 
    for (int i = start; count < newLength && i < array.length; i += stride) { 
     newArray[count++] = array[i]; 
    } 
    return newArray; 
} 

/** 
* Calculates the fast fourier transform of the given function. The parameters are updated with the calculated values 
* To ignore all imaginary output, leave imaginary null 
* @param real An array representing the real part of a discrete-time function 
* @param imaginary An array representing the imaginary part of a discrete-time function 
* Pre: If imaginary is not null, the two arrays must be the same length, which must be a power of 2 
*/ 
public void fft(double[] real, double[] imaginary) throws IllegalArgumentException { 
    if (real == null) { 
     throw new NullPointerException("Real array cannot be null"); 
    } 

    int N = real.length; 

    // Make sure the length is a power of 2 
    if ((Math.log(N)/Math.log(2)) % 1 != 0) { 
     throw new IllegalArgumentException("The array length must be a power of 2"); 
    } 

    if (imaginary != null && imaginary.length != N) { 
     throw new IllegalArgumentException("The two arrays must be the same length"); 
    } 

    if (N == 1) { 
     return; 
    } 

    double[] even_re = slice(real, 0, N/2, 2); 
    double[] odd_re = slice(real, 1, N/2, 2); 

    double[] even_im = null; 
    double[] odd_im = null; 
    if (imaginary != null) { 
     even_im = slice(imaginary, 0, N/2, 2); 
     odd_im = slice(imaginary, 1, N/2, 2); 
    } 

    fft(even_re, even_im); 
    fft(odd_re, odd_im); 

    // F[k] = real[k] + imaginary[k] 

    //    even odd 
    //  F[k] = E[k] + O[k] * e^(-i*2*pi*k/N) 
    // F[k + N/2] = E[k] - O[k] * e^(-i*2*pi*k/N) 

    // Split complex arrays into component arrays: 
    // E[k] = er[k] + i*ei[k] 
    // O[k] = or[k] + i*oi[k] 

    // e^ix = cos(x) + i*sin(x) 

    // Let x = -2*pi*k/N 
    // F[k] = er[k] + i*ei[k] + (or[k] + i*oi[k])(cos(x) + i*sin(x)) 
    //  = er[k] + i*ei[k] + or[k]cos(x) + i*or[k]sin(x) + i*oi[k]cos(x) - oi[k]sin(x) 
    //  = (er[k] + or[k]cos(x) - oi[k]sin(x)) + i*(ei[k] + or[k]sin(x) + oi[k]cos(x)) 
    //  {    real    }  {   imaginary   } 

    // F[k + N/2] = (er[k] - or[k]cos(x) + oi[k]sin(x)) + i*(ei[k] - or[k]sin(x) - oi[k]cos(x)) 
    //    {    real    }  {   imaginary   } 

    // Ignoring all imaginary parts (oi = 0): 
    //  F[k] = er[k] + or[k]cos(x) 
    // F[k + N/2] = er[k] - or[k]cos(x) 

    for (int k = 0; k < N/2; ++k) { 
     double t = odd_re[k] * Math.cos(-2 * Math.PI * k/N); 
     real[k]  = even_re[k] + t; 
     real[k + N/2] = even_re[k] - t; 

     if (imaginary != null) { 
      t = odd_im[k] * Math.sin(-2 * Math.PI * k/N); 
      real[k]  -= t; 
      real[k + N/2] += t; 

      double t1 = odd_re[k] * Math.sin(-2 * Math.PI * k/N); 
      double t2 = odd_im[k] * Math.cos(-2 * Math.PI * k/N); 
      imaginary[k]  = even_im[k] + t1 + t2; 
      imaginary[k + N/2] = even_im[k] - t1 - t2; 
     } 
    } 
} 
+3

第二張圖顯然是錯的;實際輸入應該導致對稱輸出。 –

+4

如果你想驗證你的實現(儘管浮點數值問題),只需編寫一個天真的DFT(約五行代碼),並進行比較。 –

回答

1
  1. 驗證

    這裏看看:slow DFT,iDFT末是我實現的緩慢DFT的iDFT他們經過測試和正確。我也用它們來快速實現過去的驗證。

  2. 您的代碼

    停止遞歸是錯誤的(你忘了設置返回元素)礦是這樣的:

    if (n<=1) { if (n==1) { dst[0]=src[0]*2.0; dst[1]=src[1]*2.0; } return; } 
    

    所以當你的N==1設置輸出元素Re=2.0*real[0], Im=2.0*imaginary[0]return前。此外,我有點迷失在你複雜的數學運算(t,t1,t2)和懶惰的分析。

只是要確定這裏是我的快速實施。它需要類層次結構中的太多東西,因此它不會成爲您的另一個用途,然後與您的代碼進行視覺比較。

我的快速實現(CC意味着複雜的輸出和輸入):

//--------------------------------------------------------------------------- 
void transform::DFFTcc(double *dst,double *src,int n) 
    { 
    if (n>N) init(n); 
    if (n<=1) { if (n==1) { dst[0]=src[0]*2.0; dst[1]=src[1]*2.0; } return; } 
    int i,j,n2=n>>1,q,dq=+N/n,mq=N-1; 
    // reorder even,odd (buterfly) 
    for (j=0,i=0;i<n+n;) { dst[j]=src[i]; i++; j++; dst[j]=src[i]; i+=3; j++; } 
    for ( i=2;i<n+n;) { dst[j]=src[i]; i++; j++; dst[j]=src[i]; i+=3; j++; } 
    // recursion 
    DFFTcc(src ,dst ,n2); // even 
    DFFTcc(src+n,dst+n,n2); // odd 
    // reorder and weight back (buterfly) 
    double a0,a1,b0,b1,a,b; 
    for (q=0,i=0,j=n;i<n;i+=2,j+=2,q=(q+dq)&mq) 
     { 
     a0=src[j ]; a1=+_cos[q]; 
     b0=src[j+1]; b1=+_sin[q]; 
     a=(a0*a1)-(b0*b1); 
     b=(a0*b1)+(a1*b0); 
     a0=src[i ]; a1=a; 
     b0=src[i+1]; b1=b; 
     dst[i ]=(a0+a1)*0.5; 
     dst[i+1]=(b0+b1)*0.5; 
     dst[j ]=(a0-a1)*0.5; 
     dst[j+1]=(b0-b1)*0.5; 
     } 
    } 
//--------------------------------------------------------------------------- 


dst[]src[]不重疊!!!所以你不能將數組轉換爲它自己。
_cos_sincos預計算的表和sin值(由init()計算函數是這樣的:

double a,da; int i; 
    da=2.0*M_PI/double(N); 
    for (a=0.0,i=0;i<N;i++,a+=da) { _cos[i]=cos(a); _sin[i]=sin(a); } 


N是從init(n)呼叫2(零數據集的填充大小的功率)(最後n

剛剛要完成的是我的複雜複雜慢版本:

//--------------------------------------------------------------------------- 
void transform::DFTcc(double *dst,double *src,int n) 
    { 
    int i,j; 
    double a,b,a0,a1,_n,b0,b1,q,qq,dq; 
    dq=+2.0*M_PI/double(n); _n=2.0/double(n); 
    for (q=0.0,j=0;j<n;j++,q+=dq) 
     { 
     a=0.0; b=0.0; 
     for (qq=0.0,i=0;i<n;i++,qq+=q) 
      { 
      a0=src[i+i ]; a1=+cos(qq); 
      b0=src[i+i+1]; b1=+sin(qq); 
      a+=(a0*a1)-(b0*b1); 
      b+=(a0*b1)+(a1*b0); 
      } 
     dst[j+j ]=a*_n; 
     dst[j+j+1]=b*_n; 
     } 
    } 
//--------------------------------------------------------------------------- 
+0

PS。所有代碼都是C++ – Spektre

0

我會使用像Wolfram Alpha這樣的權威來驗證。

如果我evalute cos(i/2)0 <= i < 32,我得到這個數組:

[1,0.878,0.540,0.071,-0.416,-0.801,-0.990,-0.936,-0.654,-0.211,0.284,0.709,0.960,0.977,0.754,0.347,-0.146,-0.602,-0.911,-0.997,-0.839,-0.476,0.004,0.483,0.844,0.998,0.907,0.595,0.137,-0.355,-0.760,-0.978] 

如果我給作爲輸入Wolfram Alpha的的FFT功能我得到this result

我得到的plot看起來是對稱的,這是有道理的。情節看起來沒有任何你提供的。