【问题标题】:Radix 3,5 FFT in C++C++ 中的基数 3,5 FFT
【发布时间】:2021-12-04 08:42:44
【问题描述】:

我正在尝试在 C++ 中实现 Radix-5,Radix-3 FFT,我已经设法编写了 Radix-2,但是在 Radix 3 或 5 方面我遇到了某种类型的错误,假设我这样做了3 个样本的 FFT,这将显示正确的结果,但是如果我执行 9 的 FFT,即 3 * 3,它不会显示正确的结果。

我最初从 Python 获取代码,它在那里工作,我试图简单地将其“复制”到 C++,这是原始 Python 代码:

def fft(x):
    """
    radix-2,3,5 FFT algorithm
    """
    N = len(x)
    if N <= 1:
        return x
    elif N % 2 == 0:
        # For multiples of 2 this formula works
        even = fft(x[0::2])
        odd =  fft(x[1::2])
        T = [np.exp(-2j*np.pi*k/N)*odd[k] for k in range(N//2)]
        return [even[k] + T[k] for k in range(N//2)] + \
               [even[k] - T[k] for k in range(N//2)]
    elif N % 3 == 0:
        # Optional, implementing factor 3 decimation
        p0 = fft(x[0::3])
        p1 = fft(x[1::3])
        p2 = fft(x[2::3])
        # This will construct the output output without the simplifications
        # you can do explorint symmetry
        
        for k in range(N):
            return [p0[k % (N//3)] +
                    p1[k % (N//3)] * np.exp(-2j*np.pi*k/N) + 
                    p2[k % (N//3)] * np.exp(-4j*np.pi*k/N)]
    elif N % 5 == 0:
        #factor 5 decimation
        p0 = fft(x[0::5])
        p1 = fft(x[1::5])
        p2 = fft(x[2::5])
        p3 = fft(x[3::5])
        p4 = fft(x[4::5])

        return [p0[k % (N//5)] +
                p1[k % (N//5)] * np.exp(-2j*np.pi*k/N) + 
                p2[k % (N//5)] * np.exp(-4j*np.pi*k/N) + 
                p3[k % (N//5)] * np.exp(-6j*np.pi*k/N) +
                p4[k % (N//5)] * np.exp(-8j*np.pi*k/N)
               for k in range(N)]

x = [1,1.00071,1.00135,1.00193,1.00245,1.0029,1.00329,1.00361,1.00387]
assert(np.allclose(fft(x), np.fft.fft(x)))

这是我的 C++ 代码 (fft.hpp):

#define _USE_MATH_DEFINES
#pragma once
#include <cmath>
#include <vector>
#include <complex>


using std::vector;
using std::complex;

vector<complex<float>> slicing(vector<complex<float>> vec, unsigned int X, unsigned int Y, unsigned int stride)
{
    // To store the sliced vector
    vector<complex<float>> result;

    // Copy vector using copy function()
    int i = X;
    while (result.size() < Y)
    {
        result.push_back(vec[i]);
        i = i + stride;
    }
    // Return the final sliced vector
    return result;
}

void fft(vector<complex<float>>& x)
{
    // Check if it is splitted enough

    const size_t N = x.size();
    if (N <= 1)
        return;

    else if (N % 2 == 0)
    {
        //Radix-2
        vector<complex<float>> even = slicing(x, 0, N / 2, 2); //split the inputs in even / odd indices subarrays
        vector<complex<float>>  odd = slicing(x, 1, N / 2, 2);

        // conquer
        fft(even);
        fft(odd);

        // combine
        for (size_t k = 0; k < N / 2; ++k)
        {
            complex<float> t = std::polar<float>(1.0, -2 * M_PI * k / N) * odd[k];
            x[k] = even[k] + t;
            x[k + N / 2] = even[k] - t;
        }
    }
    else if (N % 3 == 0)
    {
        //Radix-3
        //factor 3 decimation
        vector<complex<float>> p0 = slicing(x, 0, N / 3, 3);
        vector<complex<float>> p1 = slicing(x, 1, N / 3, 3);
        vector<complex<float>> p2 = slicing(x, 2, N / 3, 3);

        fft(p0);
        fft(p1);
        fft(p2);

        for (int i = 0; i < N; i++)
        {
            complex<float> temp = p0[i % (int)N / 3];
            temp += (p1[i % (int)N / 3] * std::polar<float>(1.0, -2 * M_PI * i / N));
            temp += (p2[i % (int)N / 3] * std::polar<float>(1.0, -4 * M_PI * i / N));
            x[i] = temp;
        }
    }
    else if (N % 5 == 0)
    {
        //Radix-5
        //factor 5 decimation
        vector<complex<float>> p0 = slicing(x, 0, N / 5, 5);
        vector<complex<float>> p1 = slicing(x, 1, N / 5, 5);
        vector<complex<float>> p2 = slicing(x, 2, N / 5, 5);
        vector<complex<float>> p3 = slicing(x, 3, N / 5, 5);
        vector<complex<float>> p4 = slicing(x, 4, N / 5, 5);

        fft(p0);
        fft(p1);
        fft(p2);
        fft(p3);
        fft(p4);
        for (int i = 0; i < N; i++)
        {
            complex<float> temp = p0[i % (int)N / 5];
            temp += (p1[i % (int)N / 5] * std::polar<float>(1.0, -2 * M_PI * i / N));
            temp += (p2[i % (int)N / 5] * std::polar<float>(1.0, -4 * M_PI * i / N));
            temp += (p3[i % (int)N / 5] * std::polar<float>(1.0, -6 * M_PI * i / N));
            temp += (p4[i % (int)N / 5] * std::polar<float>(1.0, -8 * M_PI * i / N));
            x[i] = temp;
        }
    }
}

还有main.cpp:

#define _USE_MATH_DEFINES
#include <stdio.h>
#include <iostream>
#include "fft.hpp"


typedef vector<complex<float>> complexSignal;

int main()
{
    complexSignal abit;
    int N = 9;
    abit.push_back({1,0});
    abit.push_back({1.00071 ,0 });
    abit.push_back({1.00135 ,0 });
    abit.push_back({1.00193 ,0 });
    abit.push_back({1.00245 ,0 });
    abit.push_back({1.0029 ,0 });
    abit.push_back({1.00329 ,0 });
    abit.push_back({1.00361 ,0 });
    abit.push_back({1.00387 ,0 });
    //abit.push_back({ 1.0029 ,0 });
    std::cout << "Before:" << std::endl;
    for (int i = 0; i < N; i++)
    {
        //abit.push_back(data[0][i]);
        std::cout << abit[i] << std::endl;
    }
    std::cout << "After:" << std::endl;
    fft(abit);
    for (int i = 0; i < N; i++)
    {
        std::cout << abit[i] << std::endl;
    }
    return 0;
}

我从 CPP 得到以下输出:

(9.02011,0)
(5.83089,-4.89513)
(0.700632,-3.98993)
(-0.000289979,0.000502368)
(-0.00218513,0.000362784)
(-0.00179241,0.00139188)
(-0.000289979,-0.000502368)
(0.000175771,-0.00354373)
(-0.003268,-0.00558837)

虽然我应该得到:

(9.020109999999999+0j)
(-0.0032675770104925446+0.005588577982060319j)
(-0.0023772289746976797+0.0024179090499282354j)
(-0.0022250000000012538+0.0011691342951078987j)
(-0.002185194014811494+0.00036271471530890747j)
(-0.0021851940148113033-0.00036271471530980844j)
(-0.0022249999999994774-0.0011691342951105632j)
(-0.002377228974696629-0.0024179090499291786j)
(-0.00326757701049002-0.005588577982061138j)

如您所见,只有第一个结果是正确的。

【问题讨论】:

  • 我没有做完整的审查,但是:用双精度浮点数替换你的浮点数,这部分看起来很可疑,在 std::polar 的浮点数中间的整数除法is i / N try : static_cast(i)/static_cast(N),还将 -2 替换为 -2.0,将 -4 替换为 -4.0 等,这也将基础类型从 int 更改为 double。
  • 底层类型从 int 到 double 是什么意思? @PepijnKramer
  • 大致:C++ 将整数视为整数。和带有 .在它作为浮点数。更完整的概述和解释可以在这里找到:learncpp.com/cpp-tutorial/literals
  • 我明白了,学习新事物总是一件好事,但是我仍在寻找答案,因为所有这些都没有解决它
  • 您是否进行了调试,并且您的所有切片都完全符合您的要求?单独测试一小部分代码总是好的。这是我编写切片代码的方式:onlinegdb.com/R021-X2zf(不要担心模板也会接受您的 std::vector<:complex>>

标签: python c++ fft mixed-radix


【解决方案1】:

在 Python 中是 k % (N//3),在 C++ 中是 i % (int)N / 3。这些不一样!见operator precedence in C++。您需要使用括号以正确的顺序执行操作:i % ((int)N / 3)

Pepijn Kramer’s comment关于改变 -2 * M_PI * i / N 进入 -2.0 * M_PI * static_cast&lt;double&gt;(i)/static_cast&lt;double&gt;(N) 在我看来,这是一种很好的做法,但在这种情况下不应该有所作为,因为从左到右的评估以及在浮点上下文中将整数自动提升为浮点数。尽管如此,确保所有常量在需要的地方都是浮点数不会花费太多精力,并且可以防止难以发现的错误。

【讨论】:

  • C++ 中的k % (N//3) 等价于什么?
  • @Cris Luengo 够公平,多年的经验告诉我要精确,顺便说一句,即使运算符的优先级很明确,这也包括添加额外的 ( )。
  • @yarinCohen 我的答案更明确了。重要的是先进行除法还是模运算。
  • @PepijnKramer 我同意你的看法。明确而准确。我很难记住运算符的优先级,所以倾向于使用大量的括号。而且我明确地转换了所有内容,让我的编译器警告隐式转换,它有时会捕获错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-10
  • 1970-01-01
  • 2017-09-28
  • 2018-08-17
  • 1970-01-01
  • 2012-08-11
相关资源
最近更新 更多