Java 实现 FFT

简介

在实现 FFT(快速傅里叶变换)之前,我们先来了解一下整个实现的流程。下面是一个简单的步骤表格:

步骤 动作
1 将输入数据分成偶数和奇数索引的两段序列
2 对奇数索引序列递归地进行 FFT
3 对偶数索引序列递归地进行 FFT
4 将两个序列的结果组合起来

现在我们逐步来实现这些步骤。

代码实现

步骤 1:将输入数据分成偶数和奇数索引的两段序列

// 输入数据的长度必须是2的整数次幂
int[] splitData(int[] input) {
    int n = input.length;
    int[] odd = new int[n / 2];
    int[] even = new int[n / 2];

    for (int i = 0; i < n; i++) {
        if (i % 2 == 0) {
            even[i / 2] = input[i];
        } else {
            odd[i / 2] = input[i];
        }
    }

    return new int[][] { odd, even };
}

上述代码中,我们首先获取输入数据的长度,并根据长度创建两个新的数组 oddeven,用于存储奇数索引和偶数索引的元素。

然后,我们使用一个循环遍历输入数组,根据索引的奇偶性将元素分配到 oddeven 数组中。

最后,我们将 oddeven 数组作为结果返回。

步骤 2:对奇数索引序列递归地进行 FFT

int[] fft(int[] odd) {
    int n = odd.length;
    if (n == 1) {
        return odd;
    }

    int[] even = new int[n];
    int[] oddReal = new int[n / 2];
    int[] oddImaginary = new int[n / 2];

    for (int i = 0; i < n; i++) {
        if (i % 2 == 0) {
            even[i] = odd[i / 2];
        } else {
            oddReal[i / 2] = odd[i];
        }
    }

    oddImaginary = fft(oddReal);

    int[] result = new int[n];
    for (int i = 0; i < n / 2; i++) {
        double angle = -2 * Math.PI * i / n;
        double real = Math.cos(angle);
        double imaginary = Math.sin(angle);

        int oddRealValue = oddReal[i];
        int oddImaginaryValue = oddImaginary[i];

        result[i] = (int) (even[i] + real * oddRealValue - imaginary * oddImaginaryValue);
        result[i + n / 2] = (int) (even[i] - real * oddRealValue + imaginary * oddImaginaryValue);
    }

    return result;
}

这段代码实现了对奇数索引序列进行 FFT 的递归操作。

首先,我们获取奇数索引序列的长度,并判断是否为 1。如果是 1,则直接返回该序列。

然后,我们创建一个新的数组 even,用于存储偶数索引的元素。同时,我们创建两个新的数组 oddRealoddImaginary,用于存储奇数索引序列的实部和虚部。

接下来,我们使用一个循环遍历奇数索引序列,根据索引的奇偶性将元素分配到 evenoddRealoddImaginary 数组中。

然后,我们递归地调用 FFT 函数,对 oddReal 数组进行 FFT 操作,并将结果存储在 oddImaginary 数组中。

接下来,我们创建一个结果数组 result,用于存储最终的结果。

最后,我们使用一个循环遍历 oddRealoddImaginary 数组,根据 FFT 公式计算每个元素的实部和虚部,并将结果存储在 result 数组中。

最终,我们将 result 数组作为结果返回。