使用 FFT 精确实现卷积的问题

计算科学 傅里叶变换 卷积
2021-12-16 02:49:16

我正在尝试执行数学定义的卷积fg(τ)=Rf(tτ)g(t)dt在数值模拟中。因此,我的信号是点的采样f(xi).

我需要计算准确、公正。确实,模拟在那里可以显示出非常精细的效果,所以我不能丢弃原则上的小错误。另请注意,此卷积将在大型阵列上执行很多次,因此它需要高效。

我的问题是我是否以及如何正确计算这个卷积?我已经尝试过已经在 J​​ulia(或 Python,同样的故事)中实现的 FFT 算法,但我似乎有系统错误,如下所示。这些是我不了解的产物吗?我还应该怎么做?

此代码是一个“测试”,用于检查卷积两个框是否以数字方式给出分析结果:

using Plots
using FFTW
using DSP


function box(x)
    if abs(x) < 0.5
        return 1
    else
        return 0
    end
end

function analytical_convolved_box(x)
    if x <= 0 && x > -1
        return x + 1
    elseif x > 0  && x < 1
        return 1 - x
    else
        return 0
    end
end

x = range(-2, stop = 2, length = 20)
dx = x[2] - x[1]

input = box.(x)

l_half = convert(Int,length(input)/2)

output = dx *conv(input, input)[l_half+1:3l_half]

plot(x, box.(x), color = :blue)
plot!(x, box.(x), seriestype = :scatter, color = :blue)
plot!(x, analytical_convolved_box.(x), color = :green)
plot!(x, analytical_convolved_box.(x), seriestype = :scatter , color = :green)
plot!(x, output, color = :black)
plot!(x, output, seriestype = :scatter, color = :black)

在此处输入图像描述

11分:

x = range(-2, stop = 2, length = 11)
dx = x[2] - x[1]

input = box.(x)

l_half = convert(Int,length(input)/2 - 0.5)

output = dx *conv(input, input)[l_half+1:3l_half+1]

在此处输入图像描述

我想清楚地了解事情。请注意,在这两个示例中,并不清楚要使用哪些索引,所以我基本上不得不尝试和猜测。

1个回答

在我看来,FFT 卷积算法正在做这里预期的事情。请记住,您使用的是离散信号,因此是离散卷积。

有几种方法可以在数值上实现离散卷积。细微的差别基本上来自于处理每个信号的有限性的方式。

两个无限阵列离散卷积f[m]g[m](和mZ) 可以写成:

(fg)[n]=mZf[m]g[nm]

对于大小的离散数组N,最简单的使用方法(充分利用 FFT)是循环卷积:

(fg)[n]=m=0N1f[m]g[(nm)mod(N)],

索引取模的地方N(所以g(1)g(N1)等等)。

这就是离散卷积的工作方式。您可以对每个数组的边界进行不同的处理(例如,用零扩展每个数组,或者无限期地重复数组的最后一个值),但在大多数情况下,它不会改变结果(尤其是在中心)。

由此,您计算的预期结果是什么?如果你拿一个数组f长度11, 和f[5]=f[6]=f[7]=1f[m]=0否则(第二个示例中的数组),那么您可以说服自己(ff)[0]=3(因为中间的三个很好地“重叠”),(ff)[1]=2(三个中只有两个“重叠”),(ff)[2]=1,(ff)[3]=0, ...

直到移动您获得的数组以使其居中,并乘以您的步长dx=0.4,这正是 FFT 算法给你的。

如果你想使用卷积的连续版本,你可能应该多取点。现在您正在使用门函数,因此理论上很容易找到确切的结果,但想象您使用其他任何东西(三角函数、幂函数、高斯函数等)。您仍然受到采样率(每单位距离的点数)的限制。程序应该如何对两点之间的缺失值进行插值?