离散傅里叶变换、快速傅里叶变换C#实现

        傅里叶变换是将时域信号变换为频域信号的一种方式,我主要用它来做两件事情:

 1 求一段数据的周期性。

 2 通过傅里叶变换及其逆变换,进行低通滤波(去躁)。

 首先需要做几点说明:

1.快速傅里叶变换是离散傅里叶变换的快速算法,当数据源较大时(大于1000),快速傅里叶变换有明显优势。

2.快速傅里叶变换的信号源长度必须是2^N(2的N次方),如果不是,则需要在末尾补0。而离散傅里叶变换没有这个要求。因此当信号源点数是2^N时,离散傅里叶变换和快速傅里叶变换的计算结果是一致的。如果长度不是2^N时,两个算法的结果是不同的,离散傅里叶变换(慢方法)更可信。

 3.下面算法中快速傅里叶变换是根据原理自创的,没有采用现在常用的位逆序的方法。

 4.下面所有方法均经过matlab验证。

 过多的理论的东西不深究,我也不是大学老师,这里直接给出离散傅里叶变换和快速傅里叶变换的C#代码:

public class FFTHelp
    {
        /// 
        /// 快速傅里叶变换(当信号源长度等于2^N时,结果与dft相同,当长度不等于2^N时,先在尾部补零,所以计算结果与dft不同)
        /// 
        /// 
        /// 
        public static double[] fft(double[] array)
        {
            array = FillArray(array);
            int N = array.Length;

            //生成WN表,以免运行时进行重复计算
            Complex[] WN = new Complex[N];
            for (int i = 0; i < N / 2; i++)
            {
                WN[i] = new Complex(Math.Cos(2 * Math.PI / N * i), -1 * Math.Sin(2 * Math.PI / N * i));
            }

            int stageNum = ReLog2N(N);
            int[] stage = new int[stageNum];
            stage[0] = 0;
            for (int i = 1; i < stageNum; i++)
            {
                stage[i] = Convert.ToInt32(Math.Round(Math.Pow(2, stageNum - 1 - i)));
            }

            //重排数据
            Complex[] Register = new Complex[N];
            for (int i = 0; i < N; i++)
            {
                int index = ReArrange(i, stageNum);
                Register[i] = new Complex(array[index], 0);
            }

            //蝶形运算
            Complex[] p = new Complex[N];
            Complex[] q = new Complex[N];
            Complex[] w = new Complex[N];
            int group = N;
            for (int i = 0; i < stageNum; i++)
            {
                group = group >> 1;
                int subnum = N / group;

                for (int k = 0; k < group; k++)
                {
                    for (int n = 0; n < subnum / 2; n++)
                    {
                        p[k * subnum + n] = p[k * subnum + n + subnum / 2] = Register[k * subnum + n];
                        w[k * subnum + n] = WN[stage[i] * n];
                    }

                    for (int n = subnum / 2; n < subnum; n++)
                    {
                        q[k * subnum + n] = q[k * subnum + n - subnum / 2] = Register[k * subnum + n];
                        w[k * subnum + n] = -1 * w[n - subnum / 2];
                    }
                }

                for (int k = 0; k < N; k++)
                {
                    Register[k] = p[k] + w[k] * q[k];
                }

            }

            double[] dest = new double[N];
            for (int k = 0; k < N; k++)
            {
                dest[k] = Register[k].Modulus();
            }
            return dest;
        }
        /// 
        /// 离散傅里叶变换
        /// 
        /// 
        /// 
        public static double[] dft(double[] array)
        {
            //array = FillArray(array);
            int N = array.Length;

            //重排数据
            Complex[] Register = new Complex[N];
            Complex[] dest = new Complex[N];
            for (int i = 0; i < N; i++)
            {
                Register[i] = new Complex(array[i], 0);
            }
            for (int k = 0; k < N; k++)
            {
                Complex sum = new Complex();
                for (int n = 0; n < N; n++)
                {
                    sum += Register[n] * (new Complex(Math.Cos(2 * Math.PI / N * n * k), -1 * Math.Sin(2 * Math.PI / N * n * k)));
                }
                dest[k] = sum;
            }

            double[] dest2 = new double[N];
            for (int k = 0; k < N; k++)
            {
                dest2[k] = dest[k].Modulus();
            }
            return dest2;
        }
        /// 
        /// 离散傅里叶逆变换
        /// 
        /// 
        /// 
        public static double[] idft(double[] array)
        {
            //array = FillArray(array);
            int N = array.Length;

            //重排数据
            Complex[] Register = new Complex[N];
            Complex[] dest = new Complex[N];
            for (int i = 0; i < N; i++)
            {
                Register[i] = new Complex(array[i], 0);
            }
            for (int k = 0; k < N; k++)
            {
                Complex sum = new Complex();
                for (int n = 0; n < N; n++)
                {
                    sum += Register[n] * (new Complex(Math.Cos(2 * Math.PI / N * n * k), -1 * Math.Sin(2 * Math.PI / N * n * k)));
                }
                dest[k] = sum / N;
            }

            double[] dest2 = new double[N];
            for (int k = 0; k < N; k++)
            {
                dest2[k] = dest[k].Modulus();
            }
            return dest2;
        }
        /// 
        /// 离散傅里叶变换
        /// 
        /// 
        /// 
        public static Complex[] dft(Complex[] array)
        {
            //array = FillArray(array);
            int N = array.Length;

            //重排数据
            Complex[] Register = array;
            Complex[] dest = new Complex[N];
            //for (int i = 0; i < N; i++)
            //{
            //    Register[i] = new Complex(array[i], 0);
            //}
            for (int k = 0; k < N; k++)
            {
                Complex sum = new Complex();
                for (int n = 0; n < N; n++)
                {
                    sum += Register[n] * (new Complex(Math.Cos(2 * Math.PI / N * n * k), -1 * Math.Sin(2 * Math.PI / N * n * k)));
                }
                dest[k] = sum;
            }

            //double[] dest2 = new double[N];
            //for (int k = 0; k < N; k++)
            //{
            //    dest2[k] = dest[k].Modulus();
            //}
            //return dest2;
            return dest;
        }

        /// 
        /// 离散傅里叶逆变换
        /// 
        /// 
        /// 
        public static Complex[] idft(Complex[] array)
        {
            //array = FillArray(array);
            int N = array.Length;

            //重排数据
            Complex[] Register = array;
            Complex[] dest = new Complex[N];
            //for (int i = 0; i < N; i++)
            //{
            //    Register[i] = new Complex(array[i], 0);
            //}
            for (int k = 0; k < N; k++)
            {
                Complex sum = new Complex();
                for (int n = 0; n < N; n++)
                {
                    sum += Register[n] * (new Complex(Math.Cos(-2 * Math.PI / N * n * k), -1 * Math.Sin(-2 * Math.PI / N * n * k)));
                }
                dest[k] = sum / N;
            }

            //double[] dest2 = new double[N];
            //for (int k = 0; k < N; k++)
            //{
            //    dest2[k] = dest[k].Modulus();
            //}
            //return dest2;
            return dest;
        }

        /// 
        /// 利用傅里叶变换实现的低通滤波(消除高频信号)
        /// 
        /// 信号源
        /// 截止频率
        /// 
        public static double[] FFTFilter(double[] array, int n)
        {
            if (array == null || n <= 0 || array.Length <= n)
                return array;

            int N = array.Length;
            Complex[] Register = new Complex[N];
            for (int i = 0; i < N; i++)
            {
                Register[i] = new Complex(array[i], 0);
            }
            Complex[] dest = dft(Register);
            for (int i = 0; i < dest.Length; i++)
            {
                if (i > n && i < dest.Length - n)
                    dest[i].Im = dest[i].Re = 0;
            }
            Complex[] dest2 = idft(dest);

            double[] result = new double[dest2.Length];
            for (int i = 0; i < dest2.Length; i++)
            {
                result[i] = dest2[i].Modulus();
            }
            return result;
        }
        private static double[] FillArray(double[] array)
        {
            //补零后长度
            int relog2N = ReLog2N(array.Length);

            int bitlenghth = relog2N;
            int N = 0x01 << bitlenghth;
            double[] ret = new double[N];
            for (int i = 0; i < N; i++)
            {
                if (i < array.Length)
                    ret[i] = array[i];
                else ret[i] = 0;
            }
            return ret;
        }

        // 获取扩展长度后的幂次
        // 由于fft要求长度为2^n,所以用此函数来获取所需长度
        public static int ReLog2N(int count)
        {
            int log2N = 0;
            uint mask = 0x80000000;
            for (int i = 0; i < 32; i++)
            {
                if (0 != ((mask >> i) & count))
                {
                    if ((mask >> i) == count) log2N = 31 - i;
                    else log2N = 31 - i + 1;
                    break;
                }
            }
            return log2N;
        }

        // 获取按位逆序,bitlenght为数据长度
        // fft函数内使用
        private static int ReArrange(int dat, int bitlenght)
        {
            int ret = 0;
            for (int i = 0; i < bitlenght; i++)
            {
                if (0 != (dat & (0x01 << i))) ret |= ((0x01 << (bitlenght - 1)) >> i);
            }
            return ret;
        }
    }
    /// 
    /// 表示一个复数
    /// 
    public class Complex
    {
        public double Re;
        public double Im;
        public Complex()
        {
            Re = 0;
            Im = 0;
        }
        public Complex(double re)
        {
            Re = re;
            Im = 0;
        }
        public Complex(double re, double im)
        {
            Re = re;
            Im = im;
        }

        public double Modulus()
        {
            return Math.Sqrt(Re * Re + Im * Im);
        }

        public override string ToString()
        {
            string retStr;
            if (Math.Abs(Im) < 0.0001) retStr = Re.ToString("f4");
            else if (Math.Abs(Re) < 0.0001)
            {
                if (Im > 0) retStr = "j" + Im.ToString("f4");
                else retStr = "- j" + (0 - Im).ToString("f4");
            }
            else
            {
                if (Im > 0) retStr = Re.ToString("f4") + "+ j" + Im.ToString("f4");
                else retStr = Re.ToString("f4") + "- j" + (0 - Im).ToString("f4");
            }
            retStr += " ";
            return retStr;
        }

        //操作符重载
        public static Complex operator +(Complex c1, Complex c2)
        {
            return new Complex(c1.Re + c2.Re, c1.Im + c2.Im);
        }
        public static Complex operator +(double d, Complex c)
        {
            return new Complex(d + c.Re, c.Im);
        }
        public static Complex operator -(Complex c1, Complex c2)
        {
            return new Complex(c1.Re - c2.Re, c1.Im - c2.Im);
        }
        public static Complex operator -(double d, Complex c)
        {
            return new Complex(d - c.Re, -c.Im);
        }
        public static Complex operator *(Complex c1, Complex c2)
        {
            return new Complex(c1.Re * c2.Re - c1.Im * c2.Im, c1.Re * c2.Im + c2.Re * c1.Im);
        }
        public static Complex operator *(Complex c, double d)
        {
            return new Complex(c.Re * d, c.Im * d);
        }
        public static Complex operator *(double d, Complex c)
        {
            return new Complex(c.Re * d, c.Im * d);
        }
        public static Complex operator /(Complex c, double d)
        {
            return new Complex(c.Re / d, c.Im / d);
        }
        public static Complex operator /(double d, Complex c)
        {
            double temp = d / (c.Re * c.Re + c.Im * c.Im);
            return new Complex(c.Re * temp, -c.Im * temp);
        }
        public static Complex operator /(Complex c1, Complex c2)
        {
            double temp = 1 / (c2.Re * c2.Re + c2.Im * c2.Im);
            return new Complex((c1.Re * c2.Re + c1.Im * c2.Im) * temp, (-c1.Re * c2.Im + c2.Re * c1.Im) * temp);
        }
    }
 如果想求一个数组的周期性,调用dft函数后,取变换后数据(去掉第一个数)的最大值索引,就是数组的频率,周期是频率的倒数。例如,数据源为数组a, b=dft(a),频率=maxindex(b),周期数T=a.length/频率。

 FFTFilter函数可直接进行低通滤波。


 

你可能感兴趣的:(算法)