You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现DFT时振幅值异常问题求助

问题:DFT计算振幅异常,高频段振幅升高

我正在开发一个通过DFT(离散傅里叶变换)结合周转圆绘制指定图形的项目,但计算出的振幅值不符合预期——无论使用何种样本点,高频段(索引i较大时)的振幅都会升高。

我的DFT函数接收存储样本点x、y坐标的double[][]数组作为输入,Points.n代表样本点总数。以下是具体实现代码:

Dft类代码

public class Dft {
    public static double[][] dft(double[][] psamples){
        double[][] result = new double[Points.n][2];
        for (int i = 0; i < Points.n; i++){
            Complex x_i = new Complex(0, 0);
            for (int j = 0; j < Points.n; j++){
                Complex sample = new Complex(psamples[j][0], psamples[j][1]);
                double root = Math.PI*2*i*j/Points.n;
                Complex unity = new Complex(Math.cos(root), -Math.sin(root));
                x_i = Complex.add(x_i, Complex.multiply(sample, unity));
            }
            result[i][0] = x_i.amp()/Points.n; //amplitude
            result[i][1] = x_i.angle(); //phase change
            //frequency is equal to the index number i
            System.out.println(i + " amp: " + result[i][0] + " phase change: " + result[i][1]);
        }
        return result;
    } 
}

Complex类代码

public class Complex {
    double[] num = {0,0};
    public Complex(double a, double b){
        num[0] = a;
        num[1] = b;
    }
    public void setReal(double a){
        num[0] = a;
    }
    public void setImaginary(double b){
        num[1] = b;
    }
    public double getReal(){
        return num[0];
    }
    public double getImaginary(){
        return num[1];
    }
    public static Complex add(Complex a, Complex b){
        Complex result = new Complex(a.getReal()+b.getReal(), a.getImaginary() + b.getImaginary());
        return result;
    }
    public static Complex euler(double r, double theta){
        Complex result = new Complex(r*Math.cos(theta), r*Math.sin(theta));
        return result;
    }
    public static Complex multiply(Complex a, Complex b){
        Complex result = new Complex(a.getReal()*b.getReal() - a.getImaginary()*b.getImaginary(), a.getReal()*b.getImaginary() + a.getImaginary()*b.getReal());
        return result;
    }
    public double amp(){
        double result = Math.sqrt(num[0]*num[0] + num[1]*num[1]);
        return result;
    }
    public double angle(){
        if (num[0] == 0){
            if (num[1]>0) return Math.PI/2;
            else if (num[1]<0) return -Math.PI/2;
            else return 0;
        }
        else {
        double result = Math.atan2(num[1],num[0]);
        return result;
        }
    }
} 

问题分析与解决方案

核心问题

问题出在DFT频率分量的振幅归一化逻辑上:
对于N点DFT,索引i对应的频率分为两种情况:

  • 当i < N/2时,是正频率分量;
  • 当i >= N/2时,是负频率分量,其振幅与对应正频率分量(索引N-i)完全相等,仅旋转方向相反。

你当前对所有索引的振幅统一除以N,但未区分正/负频率分量的能量对应关系,导致负频率分量的振幅被单独计算,看起来像是高频段振幅异常升高。

修正后的振幅计算逻辑

针对周转圆绘制场景,正确的振幅归一化规则为:

  1. 直流分量(i=0):振幅为x_i.amp()/N;
  2. 正频率分量(1 <= i < N/2):振幅为x_i.amp()*2/N(合并对应负频率分量的能量);
  3. 奈奎斯特频率分量(N为偶数时i=N/2):振幅为x_i.amp()/N(无对应负频率分量);
  4. 负频率分量(i > N/2):可直接复用对应正频率分量的振幅,或忽略(绘制周转圆仅需正频率分量)。

修改后的代码片段:

for (int i = 0; i < Points.n; i++){
    // 原有DFT计算逻辑保持不变

    if (i == 0 || (Points.n % 2 == 0 && i == Points.n/2)) {
        result[i][0] = x_i.amp() / Points.n;
    } else if (i < Points.n/2) {
        result[i][0] = x_i.amp() * 2 / Points.n;
    } else {
        // 负频率分量复用正频率振幅,或直接跳过处理
        result[i][0] = result[Points.n - i][0];
    }
    result[i][1] = x_i.angle();
}

额外优化

绘制周转圆时,仅需保留前N/2 + 1个分量(直流+正频率+奈奎斯特频率)即可,负频率分量无需参与绘制,这样能避免高频段振幅异常的视觉干扰,同时减少计算量。

内容的提问来源于stack exchange,提问作者Momin Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 05:44:55