Die Wurzel (Zur Übersicht)



www.wurzel.org



Zeitschrift   Werkstatt   Service   Verein   Hilfe  

Zeitschrift für Mathematik

Übersicht Inhalt Kontakt 


Diskrete Fourier-Transformation, Teil 2: Schnelle Berechnung
Ralph Tandetzky

Hier ist der C++-Quellcode für den im Artikel beschriebenen Algorithmus.


/***************************************************************************
 *            rtfft.h
 *
 *  Mon Feb 22 22:14:17 2010
 *  Copyright  2010  Ralph Tandetzky
 *
 *    This header file defines some useful FFT algorithms for C++.
 *  It is not meant to contain the fastest FFT algorithms ever,
 *  but is rather written for educational purposes. However, the
 *  speed is quite acceptable.
 *
 *
 *  The following functions are contained in this file:
 *
 *  DFT(In, Out, N)        calculates Discrete Fourier Transform.
 *  FFT(In, Out, N)        calculates DFT in O(N*log(N)) time.
 *  FFT_Radix2CooleyTukey(In,Out,Work,N)
 *                        Auxiliary function, that calculates DFT in O(N*log(N))
 *                        for N being a power of 2 using a preallocated working
 *                        array.
 *  FFT_Bluestein(In,Out,Work,N)
 *                        Auxiliary function, that calculates DFT in O(N*log(N))
 *                        for arbitrary N. It is multiple times slower than
 *                        FFT_Radix2CooleyTukey(...).
 *
 ****************************************************************************/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,
 * USA
 */

#ifndef __RT_FFT_H
#define __RT_FFT_H

// STL include files
#include<complex>
#include<vector>

using namespace std;

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
    Function:
          DFT

      Purpose:
          Calculate the Discrete Fourier-Transform in O(N2) time, where
          N is the number of array elements given by [N].

      Arguments:
          acIn:
              Input array of size [N].
          aOut:
              Output array. The space must be allocated. The algorithm works
            in-place as well as out-of-place, i.e. [aOut] may be
              equal to [acIn]. However, they may not overlap in any other way.
          N:
              Size of the arrays.

      Return value:
          None. The results will be stored in [aOut].
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template<class T>
void DFT(const complex<T>  *acIn, complex<T>  *aOut, size_t N)
{
    if ( acIn != aOut ) // out-of-place?
    {
        for ( size_t k=0; k<N; ++k )
        {
            complex<T>  Sum(0);
            for (size_t n=0; n<N; ++n )
                Sum+=acIn[n]*polar((T)1,(T)(-2*3.1415926535897932)*n*k/N);
            aOut[k]=Sum;
        }
    }
    else // in-place?
    {
        // Copy the contents of the input array into a temporary array and
        // execute the out-of-place-DFT for that array
        DFT(&vector<complex<T>  >(acIn, acIn+N).front(), aOut, N );
    }
}



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
    Function:
          FFT

      Purpose:
          Calculate the Discrete Fourier-Transform in O(N*log(N)) time, where
          N is the number of array elements given by [N].

      Arguments:
          acIn:
              Input array of size [N].
          aOut:
              Output array. The space must be allocated. The algorithm works
            in-place as well as out-of-place, i.e. [aOut] may be
              equal to [acIn]. However, they may not overlap in any other way.
          N:
              Size of the arrays.

      Return value:
          None. The results will be stored in [aOut].
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template<class T>
void FFT(const complex<T>  *acIn, complex<T>  *aOut, size_t N)
{
    if ( ( N&  (N-1) ) == 0 ) // size is a power of 2?
        FFT_Radix2CooleyTukey(acIn, aOut,&vector<complex<T>  >(N).front(), N);
    else // not a power of 2?
        FFT_Bluestein(acIn, aOut, N);
}



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
    Function:
          FFT_Radix2CooleyTukey

      Purpose:
          Calculate the Discrete Fourier-Transform in O(N*log(N)) time using the
          Cooley-Tukey-Algorithm, where N is the number of array elements given
          by [N]. N has to be a power of 2 or zero.

      Arguments:
          acIn:
              Input array of size [N].
          aOut:
              Output array. The space must be allocated.  The algorithm works
            in-place as well as out-of-place, i.e. [aOut] may be
              equal to [acIn]. However, they may not overlap in any other way.
          aWork:
              Array of size [N], that is used to perform the calculations.
          N:
              Size of the arrays. Must be a power of 2 or zero.

      Return value:
          None. The results will be stored in [aOut].
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template<class T>
void FFT_Radix2CooleyTukey(const complex<T>  *acIn, complex<T>  *aOut,
                           complex<T>  *aWork, size_t N)
{
    if (N == 1)
        aOut[0]=acIn[0];
    else
    {
        size_t N_2=N>>1;

        // Seperate entries with odd and even indices
        for ( size_t i=0; i<N_2; ++i )
        {
            aWork[i    ]=acIn[2*i  ];
            aWork[i+N_2]=acIn[2*i+1];
        }

        // Calculate DFTs for subarrays
        if ( N_2>  1 )
        {
            FFT_Radix2CooleyTukey(aWork    , aWork    , aOut, N_2);
            FFT_Radix2CooleyTukey(aWork+N_2, aWork+N_2, aOut, N_2);
        }

        // Put the two arrays together in the proper way
        complex<T>  MultIncrement( polar((T)1,(T)-3.1415926535798932/N_2) );
        complex<T>  TwiddleFactor( (T)1 );
        for ( size_t i=0; i<N_2; ++i )
        {
            complex<T>  A  = aWork[i];
            complex<T>  B  = TwiddleFactor*aWork[i+N_2];
            aOut[i    ]   = A + B;
            aOut[i+N_2]   = A - B;
            TwiddleFactor*= MultIncrement;
        }
    }
}



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
    Function:
          FFT_Bluestein

      Purpose:
          Calculate the Discrete Fourier-Transform in O(N*log(N)) time using the
          Bluestein-Algorithm, where N is the number of array elements given
          by [N]. N can be any positive integer, however, the algorithm takes
          much more time than the Cooley-Tukey-Algorithm.

      Arguments:
          acIn:
              Input array of size [N].
          aOut:
              Output array. The space must be allocated. The array [aOut]
              may be equal to [acIn].
          N:
              Size of the arrays. Any positive integer.

      Return value:
          None. The results will be stored in [aOut].
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template<class T>
void FFT_Bluestein(const complex<T>  *acIn, complex<T>  *aOut, size_t N)
{
    // BUG WORKAROUND: The actual algorithm doesn't work for odd N. It yields
    // uncorrect results for unknown reasons. Therefore we double the size to
    // keep the O(N*log(N)) time complexity and still get correct results.
    if (N % 2 == 1) // N odd?
    {
        vector<complex<T>  >  vInOut2( 2*N, complex<T>(0) );
        copy( acIn, acIn+N, vInOut2.begin() ); // copy the original input array
        FFT_Bluestein(&vInOut2.front(),&vInOut2.front(), 2*N );
        for ( size_t i=0; i<N; i++ )
            aOut[i] = vInOut2[2*i];
    }
    else // N even?
    {
        // Calculate M as the smallest power of 2 that is at least 2*N, where
        // N is the vector size.
        size_t M=N;
        M--; M|=M>>1; M|=M>>2; M|=M>>4; M|=M>>8; M|=M>>16; M++; M<<=1;

        // Calculate the zero-padded a and b.
        vector<complex<T>  >  a(M,0), b(M,0);
        const T MinAngle=(T)3.1415926535897932/N;
        for ( size_t i=0; i<N; ++i )
        {
            b[i]=polar((T)1,i*i*MinAngle);
            a[i]=conj(b[i])*acIn[i];
        }
        vector<complex<T>  >  bNonZeroPadded(b.begin(), b.begin()+N);

        // Apply Cooley-Tukey-Algorithm to a and b
        vector<complex<T>  >  Work(M);
        FFT_Radix2CooleyTukey(&a.front(),&a.front(),&Work.front(),M);
        FFT_Radix2CooleyTukey(&b.front(),&b.front(),&Work.front(),M);

        // Multiply F(a) and F(b) pointwise, assign to a
        for ( size_t i=0; i<M; ++i )
            a[i] *= b[i];

        // back-transform this product. Calculate convolution of
        // non-zero-padded a and b.
        FFT_Radix2CooleyTukey(&a.front(),&a.front(),&Work.front(),M);
        // Now a contains the convolution the zero-padded a and b times M
        // backwards.
        const T Frac1M=(T)1/M;
        aOut[0] = (a[0]+a[M-N])*Frac1M;
        for ( size_t i=1; i<N; ++i )
            aOut[i] = conj(bNonZeroPadded[i])*(a[M-i]+a[M-i-N])*Frac1M;
    }
}


#endif //__RT_FFT_H

      

> Die Wurzel > Zeitschrift

Anfang

Übersicht  Zeitschrift  Werkstatt  Service  Verein  Hilfe
eMail  Kontakt zur Wurzel   Feedback zur Website Datenschutz


© 1996-2020 Wurzel e.V. Alle Rechte vorbehalten.