Python/NumPy - 二维FFT无法得到解析解

编程语言 2026-07-10

我正在编写一个代码,作为第一步计算一个函数的二维FFT。我用函数exp(-r)/r进行测试,其中r = sqrt(x²+y²),它的解析Hankel变换是1/sqrt(r²+1)。零阶Hankel变换与二维傅里叶变换之间通过一个重新缩放相关联。然而,我无法恢复这个解析解。我的变换比它更窄,且有非零的虚部。

最小可复现示例:

import numpy as np
import matplotlib.pyplot as pl


def min_pot(x: np.ndarray, y: np.ndarray):
    xbig, ybig = np.meshgrid(x, y)
    r = np.sqrt(np.square(xbig) + np.square(ybig))
    r0 = np.unravel_index(np.argmin(r, axis=None), r.shape)
    test = np.exp(-r)*np.reciprocal(r)
    test[r0] = 2*test[r0[0]+1, r0[0]] - test[r0[0]+2, r0[0]]  #this suppresses the r=0 singularity
    return test


x_min = np.linspace(-20, 20, num=1001)
y_min = np.linspace(-20, 20, num=1001)


real_a = min_pot(x_min, y_min)
shift_a = np.fft.ifftshift(real_a)
fourier_a = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(real_a), norm="forward"))
kx = np.fft.fftshift(np.fft.fftfreq(len(x_min), d=(x_min[1]-x_min[0])))

#we represent a cut in the x axis

analytic = np.power(np.square(kx) + 1, -.5)

max_real = np.amax(fourier_a[fourier_a.shape[0]//2, :].real  # normalization to compare curve shapes better
fig, ax = pl.subplots(1, 1)
ax.plot(kx, fourier_a[fourier_a.shape[0]//2, :].real/max_real), label='Real fft')
ax.plot(kx, fourier_a[fourier_a.shape[0]//2, :].imag/max_imag), label='Imag. fft')
ax.plot(kx, analytic, label='Analytic')
pl.legend()
pl.show()

结果图是:数值结果与解析结果对比

有人知道我到底哪里做错了吗?

解决方案

这是一个不完整的回答,因为我无法准确看到你到底在做错什么,也不使用Python。凭直觉从你的图看,FFT的结果在宽度上似乎被放大了 1/2pi 倍,所以我在Excel做了一个快速探索性模型,以你的图为背景。解析函数普适化到 f(x,a) = a/sqrt(a^2+x^2),因此它可以很容易生成一个归一化到 1 的图,且具有变化的全宽半最大值(FWHM)。

你的解析形式对于 a=1 来说是完全正确的。我的粗略模型在X 值较小时能很好拟合FFT结果,但当X 增大时发散,因为该函数理应渐近趋向于 a/x,但并非如此。

Hankel transform analytic solution scaled

我把Excel的解析解 1/sqrt(1+x^2) 用细黑线绘在绿色之上以强调拟合,同时用红色标出对FFT结果的最佳(肉眼)拟合点。请注意,当 x = 10 时,尺度为宽度的解析解的翼缘仍然相当高,达到约 0.016。我还把拟合在 x=10 限制,以便展示背后原始曲线的一部分。

神秘的是,你基于FFT的计算收敛得太好。它低于解析解,迅速触及零基线,而我本以为混叠会让它略高于(解析结果)。

在较大 x 处应该有更高的基线,因为函数在缓慢下降。非零的虚部几乎可以被视为在问题设定中的对称性错误的象限标记。一个偶函数在其FFT中永远不可能有虚部(除非是累积舍入误差)。

要纠正数组的对称性,可以尝试只用10、11或 100、101个元素来查看并打印它们,以发现象限标记的错位。我怀疑这样的修正可能不足以解决问题,但应在大致的 x 数值区间内提高一致性。大约一百个点在这个范围内的变换看起来应该足够,十个可能太少。

不妨用 N=1000 或任意其他偶数进行代码尝试,看看相位误差是否仍然存在。否则,由于你知道输入函数是纯实数的,理想情况下应使用FFT的实部到共轭复数形式变换 np.rfft2,它将使用一半的内存、速度更快,且可能略微更准确。如果你今后经常执行这种变换,我强烈推荐后者。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章