可可西

Cooley-Tukey FFT算法的非递归实现

一维情况

#include <iostream>

#include <complex>
#include <cmath>

const double PI = 3.14159265358979323846;

// 交换位置 
void swap(std::complex<double>& a, std::complex<double>& b) {
    std::complex<double> temp = a;
    a = b;
    b = temp;
}

// 频率倒位 
void bitReverse(std::complex<double>* point, int N) {
    int j = 0;
    for (int i = 1; i < N - 1; ++i) {
        int k = N / 2;
        while (j >= k) {
            j -= k;
            k /= 2;
        }
        j += k;
        if (i < j) {
            swap(point[i], point[j]);
        }
    }
}

// Cooley-Tukey算法的非递归实现
void FFT(std::complex<double>* point, int N) {
    bitReverse(point, N);

    for (int s = 2; s <= N; s *= 2) { // 蝶形运算的Stage循环
        double angle = -2 * PI / s;
        std::complex<double> w(1, 0);
        std::complex<double> wn(cos(angle), sin(angle));

        for (int k = 0; k < N; k += s) {  // 一组蝶形运算group,注:每个group的蝶形因子乘数不同
            w = std::complex<double>(1, 0);
            for (int j = 0; j < s / 2; ++j) { // 一个蝶形运算  注:group内的蝶形运算
                std::complex<double> u = point[k + j];
                std::complex<double> v = point[k + j + s / 2] * w;
                point[k + j] = u + v;
                point[k + j + s / 2] = u - v;

                w *= wn;
            }
        }
    }
}

// FFT逆变换
void IFFT(std::complex<double>* point, int N) {
    // 共轭变换(即:实部不变,虚部取相反数)
    for (int i = 0; i < N; ++i) {
        point[i] = std::conj(point[i]);
    }

    // 应用FFT
    FFT(point, N);

    // 共轭变换(即:实部不变,虚部取相反数)并归一化
    for (int i = 0; i < N; ++i) {
        point[i] = std::conj(point[i]);
        point[i] /= N;
    }
}

// FFT变换测试
void FFTTest()
{
    const int N = 8;  // 输入序列的长度
    std::complex<double> input[N] = { 0, 1, 2, 3, 4, 5, 6, 7 };  // 输入序列

    // 将输入序列扩展为2的幂次方长度
    int paddedLength = (int)pow(2, ceil(log2(N)));
    std::complex<double>* paddedInput = new std::complex<double>[paddedLength];
    for (int i = 0; i < N; ++i) {
        paddedInput[i] = input[i];
    }
    // 补零到2的n次幂
    for (int i = N; i < paddedLength; ++i) {
        paddedInput[i] = 0;
    }

    // 执行FFT变换
    FFT(paddedInput, paddedLength);

    std::cout << "========== FFT变换 ========== " << std::endl;

    // 输出原始数据
    std::cout << "原始数据:----------" << std::endl;
    for (int k = 0; k < N; ++k) {
        std::cout << "f[" << k << "] = " << input[k] << std::endl;
    }

    // 输出变换结果
    std::cout << "变换结果:----------" << std::endl;
    for (int k = 0; k < paddedLength; ++k) {
        std::cout << "F[" << k << "] = " << paddedInput[k] << std::endl;
    }

    // 输出幅值结果
    std::cout << "幅值结果:----------" << std::endl;
    for (int k = 0; k < paddedLength; ++k) {
        std::cout << "Length[" << k << "] = " << std::abs(paddedInput[k]) << std::endl; // 计算复数的模
    }

    delete[] paddedInput;
}

// FFT逆变换测试
void IFFTTest()
{
    const int N = 8;  // 输入序列的长度
    std::complex<double> input[N] = {
        {28,0}, {-4,9.65685}, {-4,4}, {-4,1.65685},
        {-4,0}, {-4,-1.65685}, {-4,-4}, {-4,-9.65685}
    };  // 输入序列

    // 将输入序列扩展为2的幂次方长度
    int paddedLength = (int)pow(2, ceil(log2(N)));
    std::complex<double>* paddedInput = new std::complex<double>[paddedLength];
    for (int i = 0; i < N; ++i) {
        paddedInput[i] = input[i];
    }
    // 补零到2的n次幂
    for (int i = N; i < paddedLength; ++i) {
        paddedInput[i] = 0;
    }

    // 执行FFT逆变换
    IFFT(paddedInput, paddedLength);

    std::cout << "========== FFT逆变换 ========== " << std::endl;

    // 输出原始数据
    std::cout << "原始数据:----------" << std::endl;
    for (int k = 0; k < N; ++k) {
        std::cout << "F[" << k << "] = " << input[k] << std::endl;
    }

    // 输出变换结果
    std::cout << "变换结果:----------" << std::endl;
    for (int k = 0; k < paddedLength; ++k) {
        std::cout << "f[" << k << "] = " << paddedInput[k] << std::endl;
    }

    delete[] paddedInput;
}

int main() {
    
    // 执行FFT变换测试
    FFTTest();

    std::cout << std::endl;

    // 执行FFT逆变换测试
    IFFTTest();
    return 0;
}

 

输出结果如下:

========== FFT变换 ==========
原始数据:----------
f[0] = (0,0)
f[1] = (1,0)
f[2] = (2,0)
f[3] = (3,0)
f[4] = (4,0)
f[5] = (5,0)
f[6] = (6,0)
f[7] = (7,0)
变换结果:----------
F[0] = (28,0)
F[1] = (-4,9.65685)
F[2] = (-4,4)
F[3] = (-4,1.65685)
F[4] = (-4,0)
F[5] = (-4,-1.65685)
F[6] = (-4,-4)
F[7] = (-4,-9.65685)
幅值结果:----------
Length[0] = 28
Length[1] = 10.4525
Length[2] = 5.65685
Length[3] = 4.32957
Length[4] = 4
Length[5] = 4.32957
Length[6] = 5.65685
Length[7] = 10.4525

========== FFT逆变换 ==========
原始数据:----------
F[0] = (28,0)
F[1] = (-4,9.65685)
F[2] = (-4,4)
F[3] = (-4,1.65685)
F[4] = (-4,0)
F[5] = (-4,-1.65685)
F[6] = (-4,-4)
F[7] = (-4,-9.65685)
变换结果:----------
f[0] = (0,-0)
f[1] = (1,1.72255e-16)
f[2] = (2,4.44089e-16)
f[3] = (3,3.82857e-16)
f[4] = (4,-0)
f[5] = (5,-4.979e-17)
f[6] = (6,-4.44089e-16)
f[7] = (7,-5.05322e-16)

 

二维情况

二维FFT是在一维FFT基础上实现,实现过程为:

1.对二维输入数据的每一行进行FFT,变换结果仍然按行存入二维数组中。

2.在1的结果基础上,对每一列进行FFT,再存入原来数组,即得到二维FFT结果。

#include <iostream>
#include <complex>
#include <cmath>

const double PI = 3.14159265358979323846;

// 交换位置
void swap(std::complex<double>& a, std::complex<double>& b) {
    std::complex<double> temp = a;
    a = b;
    b = temp;
}

// 频率倒位
void bitReverse(std::complex<double>* point, int N) {
    int j = 0;
    for (int i = 1; i < N - 1; ++i) {
        int k = N / 2;
        while (j >= k) {
            j -= k;
            k /= 2;
        }
        j += k;
        if (i < j) {
            swap(point[i], point[j]);
        }
    }
}

// 一维Cooley-Tukey算法的非递归实现
void FFT(std::complex<double>* point, int N) {
    bitReverse(point, N);
    for (int s = 2; s <= N; s *= 2) {
        double angle = -2 * PI / s;
        std::complex<double> w(1, 0);
        std::complex<double> wn(cos(angle), sin(angle));
        for (int k = 0; k < N; k += s) {
            w = std::complex<double>(1, 0);
            for (int j = 0; j < s / 2; ++j) {
                std::complex<double> u = point[k + j];
                std::complex<double> v = point[k + j + s / 2] * w;
                point[k + j] = u + v;
                point[k + j + s / 2] = u - v;
                w *= wn;
            }
        }
    }
}

// 一维FFT逆变换
void IFFT(std::complex<double>* point, int N) {
    // 共轭变换(即:实部不变,虚部取相反数)
    for (int i = 0; i < N; ++i) {
        point[i] = std::conj(point[i]);
    }

    // 应用FFT
    FFT(point, N);

    // 共轭变换(即:实部不变,虚部取相反数)并归一化
    for (int i = 0; i < N; ++i) {
        point[i] = std::conj(point[i]);
        point[i] /= N;
    }
}

// 二维Cooley-Tukey算法的非递归实现
void FFT2D(std::complex<double>* point, int rows, int cols) {
    // 对每一行进行FFT变换
    for (int i = 0; i < rows; ++i) {
        FFT(&point[i * cols], cols);
    }

    // 对每一列进行FFT变换
    std::complex<double>* column = new std::complex<double>[rows];
    for (int j = 0; j < cols; ++j) {

        for (int i = 0; i < rows; ++i) {
            column[i] = point[i * cols + j];
        }
        FFT(column, rows);
        for (int i = 0; i < rows; ++i) {
            point[i * cols + j] = column[i];
        }
    }
    delete[] column;
}

// 二维FFT逆变换
void IFFT2D(std::complex<double>* point, int rows, int cols) {
    // 对每一行进行逆变换
    for (int i = 0; i < rows; ++i) {
        IFFT(&point[i * cols], cols);
    }

    // 对每一列进行逆变换
    std::complex<double>* column = new std::complex<double>[rows];
    for (int j = 0; j < cols; ++j) {

        for (int i = 0; i < rows; ++i) {
            column[i] = point[i * cols + j];
        }
        IFFT(column, rows);
        for (int i = 0; i < rows; ++i) {
            point[i * cols + j] = column[i];
        }
    }
    delete[] column;
}

// FFT变换测试
void FFTTest()
{
    const int rows = 4;  // 输入矩阵的行数
    const int cols = 4;  // 输入矩阵的列数
    std::complex<double> input[rows * cols] = {
        1, 2, 3, 4,
        5, 6, 7, 8,
        9, 10, 11, 12,
        13, 14, 15, 16
    };  // 输入矩阵

    // 将输入矩阵的行数和列数扩展为2的幂次方长度
    int paddedRows = (int)pow(2, ceil(log2(rows)));
    int paddedCols = (int)pow(2, ceil(log2(cols)));

    // 将长宽补零到2的n次幂
    std::complex<double>* paddedInput = new std::complex<double>[paddedRows * paddedCols];
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            paddedInput[i * paddedCols + j] = input[i * cols + j];
        }
        for (int j = cols; j < paddedCols; ++j) {
            paddedInput[i * paddedCols + j] = 0;
        }
    }
    for (int i = rows; i < paddedRows; ++i) {
        for (int j = 0; j < paddedCols; ++j) {
            paddedInput[i * paddedCols + j] = 0;
        }
    }

    // 执行2D FFT变换
    FFT2D(paddedInput, paddedRows, paddedCols);

    std::cout << "========== FFT变换 ========== " << std::endl;

    // 输出原始数据
    std::cout << "原始数据:----------" << std::endl;
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            std::cout << "f[" << i << "][" << j << "] = " << input[i * cols + j] << std::endl;
        }
    }

    // 输出变换结果
    std::cout << "变换结果:----------" << std::endl;
    for (int i = 0; i < paddedRows; ++i) {
        for (int j = 0; j < paddedCols; ++j) {
            std::cout << "F[" << i << "][" << j << "] = " << paddedInput[i * paddedCols + j] << std::endl;
        }
    }

    // 输出幅值结果
    std::cout << "幅值结果:----------" << std::endl;
    for (int i = 0; i < paddedRows; ++i) {
        for (int j = 0; j < paddedCols; ++j) {
            std::cout << "Length[" << i << "][" << j << "] = " << std::abs(paddedInput[i * paddedCols + j]) << std::endl; // 计算复数的模
        }
    }

    delete[] paddedInput;
}

// FFT逆变换测试
void IFFTTest()
{
    const int rows = 4;  // 输入矩阵的行数
    const int cols = 4;  // 输入矩阵的列数
    std::complex<double> input[rows * cols] = {
        {136,0}, {-8,8}, {-8,0}, {-8,-8},
        {-32,32}, {0,0}, {0,0}, {0,0},
        {-32,0}, {0,0}, {0,0}, {0,0},
        {-32,-32}, {0,0}, {0,0}, {0,0}
    };  // 输入矩阵

    // 将输入矩阵的行数和列数扩展为2的幂次方长度
    int paddedRows = (int)pow(2, ceil(log2(rows)));
    int paddedCols = (int)pow(2, ceil(log2(cols)));

    // 将长宽补零到2的n次幂
    std::complex<double>* paddedInput = new std::complex<double>[paddedRows * paddedCols];
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            paddedInput[i * paddedCols + j] = input[i * cols + j];
        }
        for (int j = cols; j < paddedCols; ++j) {
            paddedInput[i * paddedCols + j] = 0;
        }
    }
    for (int i = rows; i < paddedRows; ++i) {
        for (int j = 0; j < paddedCols; ++j) {
            paddedInput[i * paddedCols + j] = 0;
        }
    }

    // 执行2D FFT变换
    IFFT2D(paddedInput, paddedRows, paddedCols);

    std::cout << "========== 执行FFT逆变换 ========== " << std::endl;

    // 输出原始数据
    std::cout << "原始数据:----------" << std::endl;
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            std::cout << "F[" << i << "][" << j << "] = " << input[i * cols + j] << std::endl;
        }
    }

    // 输出变换结果
    std::cout << "变换结果:----------" << std::endl;
    for (int i = 0; i < paddedRows; ++i) {
        for (int j = 0; j < paddedCols; ++j) {
            std::cout << "f[" << i << "][" << j << "] = " << paddedInput[i * paddedCols + j] << std::endl;
        }
    }

    delete[] paddedInput;
}

int main() {

    // 执行FFT变换测试
    FFTTest();

    std::cout << std::endl;

    // 执行FFT逆变换测试
    IFFTTest();

    return 0;
}

 

输出结果如下:

========== FFT变换 ==========
原始数据:----------
f[0][0] = (1,0)
f[0][1] = (2,0)
f[0][2] = (3,0)
f[0][3] = (4,0)
f[1][0] = (5,0)
f[1][1] = (6,0)
f[1][2] = (7,0)
f[1][3] = (8,0)
f[2][0] = (9,0)
f[2][1] = (10,0)
f[2][2] = (11,0)
f[2][3] = (12,0)
f[3][0] = (13,0)
f[3][1] = (14,0)
f[3][2] = (15,0)
f[3][3] = (16,0)
变换结果:----------
F[0][0] = (136,0)
F[0][1] = (-8,8)
F[0][2] = (-8,0)
F[0][3] = (-8,-8)
F[1][0] = (-32,32)
F[1][1] = (0,0)
F[1][2] = (0,0)
F[1][3] = (0,0)
F[2][0] = (-32,0)
F[2][1] = (0,0)
F[2][2] = (0,0)
F[2][3] = (0,0)
F[3][0] = (-32,-32)
F[3][1] = (0,0)
F[3][2] = (0,0)
F[3][3] = (0,0)
幅值结果:----------
Length[0][0] = 136
Length[0][1] = 11.3137
Length[0][2] = 8
Length[0][3] = 11.3137
Length[1][0] = 45.2548
Length[1][1] = 0
Length[1][2] = 0
Length[1][3] = 0
Length[2][0] = 32
Length[2][1] = 0
Length[2][2] = 0
Length[2][3] = 0
Length[3][0] = 45.2548
Length[3][1] = 0
Length[3][2] = 0
Length[3][3] = 0

========== 执行FFT逆变换 ==========
原始数据:----------
F[0][0] = (136,0)
F[0][1] = (-8,8)
F[0][2] = (-8,0)
F[0][3] = (-8,-8)
F[1][0] = (-32,32)
F[1][1] = (0,0)
F[1][2] = (0,0)
F[1][3] = (0,0)
F[2][0] = (-32,0)
F[2][1] = (0,0)
F[2][2] = (0,0)
F[2][3] = (0,0)
F[3][0] = (-32,-32)
F[3][1] = (0,0)
F[3][2] = (0,0)
F[3][3] = (0,0)
变换结果:----------
f[0][0] = (1,-0)
f[0][1] = (2,6.12323e-17)
f[0][2] = (3,-0)
f[0][3] = (4,-6.12323e-17)
f[1][0] = (5,2.44929e-16)
f[1][1] = (6,3.06162e-16)
f[1][2] = (7,2.44929e-16)
f[1][3] = (8,1.83697e-16)
f[2][0] = (9,-0)
f[2][1] = (10,6.12323e-17)
f[2][2] = (11,-0)
f[2][3] = (12,-6.12323e-17)
f[3][0] = (13,-2.44929e-16)
f[3][1] = (14,-1.83697e-16)
f[3][2] = (15,-2.44929e-16)
f[3][3] = (16,-3.06162e-16)

 

扩展阅读

C++实现二维快速傅里叶变换(FFT)

 

posted on 2024-09-08 23:23  可可西  阅读(33)  评论(0编辑  收藏  举报

导航