这个傅立叶变换实现有什么问题

时间:2011-04-19 11:45:30

标签: c# c#-4.0 fft complex-numbers

我正在尝试实现离散傅立叶变换,但它不起作用。我可能已经在某个地方写了一个bug,但我还没有找到它。

基于以下公式:

terere

此函数执行第一个循环,循环遍历X0 - Xn-1 ... enter image description here

    public Complex[] Transform(Complex[] data, bool reverse)
    {
        var transformed = new Complex[data.Length];
        for(var i = 0; i < data.Length; i++)
        {
            //I create a method to calculate a single value
            transformed[i] = TransformSingle(i, data, reverse);
        }
        return transformed;
    }

实际计算,这可能是错误所在。

    private Complex TransformSingle(int k, Complex[] data, bool reverse)
    {
        var sign = reverse ? 1.0: -1.0;
        var transformed = Complex.Zero;
        var argument = sign*2.0*Math.PI*k/data.Length;
        for(var i = 0; i < data.Length; i++)
        {
            transformed += data[i]*Complex.FromPolarCoordinates(1, argument*i);
        }
        return transformed;
    }

接下来解释剩下的代码:

var sign = reverse ? 1.0: -1.0;反向DFT在参数中不会有-1,而常规DFT在参数中确实有-1

enter image description here

var argument = sign*2.0*Math.PI*k/data.Length;是算法的参数。这部分:

enter image description here

然后是最后一部分

transformed += data[i]*Complex.FromPolarCoordinates(1, argument*i);

我认为我仔细地复制了算法,所以我没有看到我犯错的地方......

其他信息

正如Adam Gritt在他的回答中所表明的那样,AForge.net对这个算法有很好的实现。我可以通过复制代码在30秒内解决这个问题。但是,我仍然不知道我在实施中做错了什么。

我真的很好奇我的缺陷在哪里,以及我的解释错误。

2 个答案:

答案 0 :(得分:5)

我做复杂数学的日子现在已经落后于我,所以我自己可能会遗漏一些东西。但是,在我看来,您正在执行以下操作:

transformed += data[i]*Complex.FromPolarCoordinates(1, argument*i);

它应该更像是:

transformed += data[i]*Math.Pow(Math.E, Complex.FromPolarCoordinates(1, argument*i));

除非你已将其包含在方法FromPolarCoordinates()

更新: 我在AForge.NET Framework库中找到了以下代码,它显示了正在执行的其他Cos / Sin操作,这些操作未在代码中处理。可以在Sources \ Math \ FourierTransform.cs:DFT方法的完整上下文中找到此代码。

for ( int i = 0; i < n; i++ )
{
    dst[i] = Complex.Zero;

    arg = - (int) direction * 2.0 * System.Math.PI * (double) i / (double) n;

    // sum source elements
    for ( int j = 0; j < n; j++ )
    {
        cos = System.Math.Cos( j * arg );
        sin = System.Math.Sin( j * arg );

        dst[i].Re += ( data[j].Re * cos - data[j].Im * sin );
        dst[i].Im += ( data[j].Re * sin + data[j].Im * cos );
    }
}

它使用的是自定义Complex类(因为它是4.0之前的版本)。大多数数学运算与您实现的类似,但内部迭代正在对Real和Imaginary部分进行额外的数学运算。

进一步更新: 经过一些实施和测试后,我发现上面的代码和问题中提供的代码产生了相同的结果。我还根据评论发现,这段代码生成的内容与WolframAlpha生成的内容之间存在差异。结果的不同之处在于看起来Wolfram正在对结果应用1 / sqrt(N)的归一化。在Wolfram Link中,如果每个值乘以Sqrt(2),那么这些值与上面代码生成的值相同(除了舍入误差)。我通过将3个,4个和5个值传递给Wolfram来测试这一点,并且发现我的结果因Sqrt(3),Sqrt(4)和Sqrt(5)而有所不同。根据维基百科提供的Discrete Fourier Transform信息,它确实提到了使DFT和IDFT的变换统一的规范化。这可能是您需要查看修改代码或了解Wolfram可能正在做什么的途径。

答案 1 :(得分:1)

你的代码实际上几乎是正确的(你在逆变换上缺少1 / N)。问题是,您使用的公式通常用于计算,因为它更轻,但在纯粹的理论环境(和Wolfram)中,您将使用1 / sqrt(N)的归一化来使变换成为单一的。

即。你的公式将是:

Xk = 1/sqrt(N) * sum(x[n] * exp(-i*2*pi/N * k*n))

x[n] = 1/sqrt(N) * sum(Xk * exp(i*2*pi/N * k*n))

只是常规化的常规问题,只有幅度发生变化,因此你的结果并不那么糟糕(如果你没有忘记反向变换中的1 / N)。

干杯