【问题标题】:What is wrong with this fourier transform implementation这个傅立叶变换实现有什么问题
【发布时间】:2011-04-19 11:45:29
【问题描述】:

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

基于以下公式:

此函数执行第一个循环,循环 X0 - Xn-1...

    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;
    }

而实际计算,这大概就是bug所在。

    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

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

然后是最后一部分

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

我想我是仔细复制了算法,所以我看不出我在哪里犯了错误......

其他信息

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

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

【问题讨论】:

  • 无论如何,你已经很好地提出了这个问题。 +1。
  • 哈哈谢谢。实际上,我希望通过提出这样的问题,我自己会发现错误。但是太糟糕了xD
  • 呃...不要拖钓(太多):但实现是在 C# 中?我很好奇任何基准测试结果...
  • 哦呵呵 xD 这不是为了快速的结果。如果我想要,我会使用一个好的其他库。我只是想学习和理解这一点。我也会做一个 FFT,然后尝试优化它。但首先我想知道为什么这不起作用:p
  • 换句话说,它非常慢

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


【解决方案1】:

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

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 之前的版本)。大多数数学运算与您实现的相似,但内部迭代正在对实部和虚部进行额外的数学运算。

进一步更新: 经过一些实现和测试,我发现上面的代码和问题中提供的代码产生了相同的结果。我还发现,基于 cmets,此代码生成的内容与 WolframAlpha 生成的内容之间有什么区别。结果的不同之处在于,Wolfram 似乎对结果应用了 1/sqrt(N) 的归一化。在所提供的 Wolfram Link 中,如果每个值都乘以 Sqrt(2),则这些值与上述代码生成的值相同(舍入误差除外).我通过将 3、4 和 5 个值传递给 Wolfram 对此进行了测试,发现我的结果分别与 Sqrt(3)、Sqrt(4) 和 Sqrt(5) 不同。根据 wikipedia 提供的Discrete Fourier Transform 信息,它确实提到了一种标准化,以使 DFT 和 IDFT 的变换成为单一的。这可能是您需要查看的途径以修改您的代码或了解 Wolfram 可能在做什么。

【讨论】:

  • 我相信这不会解决它。但是话又说回来,我可能是错的 xD 但是转换后是一个复数,并且 Math.Pow() 返回一个实数。此外,Complex.FromPolarCoordinates 根据极坐标返回一个新的复数。
  • 正如你在这里看到的,我们不应该对 e 做任何事情,因为它只是写复数(欧拉公式)的另一种方式upload.wikimedia.org/math/1/2/6/…
  • 好老欧拉。就像我说的,我复杂的数学技能落后于我。基于欧拉公式 e^ix = cos x + i sin x 和en.wikipedia.org/wiki/Polar_coordinate_system,看来您可能应该做 Complex.FromPolarCoordinates(argumenti, argumenti)。如果没有看到 FromPolarCoordinates 中的代码来全面了解数据的其他转换,我不知道还有什么问题。如果这没有帮助,我很抱歉。说实话,我已经 20 多年没有与 Polars 合作了。
  • 啊,我明白了,但是 Complex.FromPolarCoordinates 只是 Complex 结构 msdn.microsoft.com/en-us/library/system.numerics.complex.aspx 中的一个函数。它只是根据它的论点和大小创建一个复杂的结构。不,非常感谢 :D 你在帮助我 :D :D
  • 我想我说错了。您可以在极坐标中表示一个复数,如下所示:re^iφ 与:r = 幅度,和 φ = 参数。在这种情况下,幅度为1,参数为argument*i。而FromPolarCoordinates 只返回一个复数,它以极坐标作为输入。
【解决方案2】:

您的代码实际上几乎是正确的(您在逆变换中缺少 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)。

干杯

【讨论】:

  • 嘿,谢谢,我确实没有在逆变换中包含 1/N(不想让它变得更复杂)。我现在就去测试一下。多么奇怪,我知道标准化。但我没有看到它记录在 wolfram alpha/mathworld :)
  • 你在开玩笑吗 xD 哈哈,它现在产生的结果与 WolframAlpha 相同 :)
猜你喜欢
  • 1970-01-01
  • 2015-06-21
  • 2015-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多