fftconvolve输出异常:卷积结果负区间出现非零概率密度问题
卷积概率分布后负区间出现非零值的解决方法
问题场景
我定义了两个概率分布:
- 在区间
[-0.05, 0)上概率密度为0 - 在区间
[0,1]上通过插值生成,实现代码如下:
import matplotlib.pyplot as plt import numpy as np from scipy import signal step = 1e-3 x = np.arange(-0.05, 1, step) x_0minus = x[x<0] x_0plus = x[x>0] pdf1 = np.concatenate([np.repeat(0, len(x_0minus)), np.interp(x_0plus, [0, 0.08, 0.28], [0, 80, 0])]) pdf2 = np.concatenate([np.repeat(0, len(x_0minus)), np.interp(x_0plus, [0, 0.1, 0.3, 0.31], [60, 60, 60, 0])])
已通过以下代码绘制两个分布的参考图:
plt.scatter(x = x, y = pdf1)(对应Pdf1图像)plt.scatter(x = x, y = pdf2)(对应Pdf2图像)
遇到的问题
使用fftconvolve对两个归一化后的分布做卷积时,结果在0以下的区间出现了非零概率密度,卷积代码如下:
res = signal.fftconvolve(pdf1 / pdf1.sum(), pdf2 / pdf2.sum(), 'same') plt.scatter(x = x, y = res)
我希望**不将计算范围扩展到[-1,1]**的前提下解决这个问题,避免浪费计算资源。
原因分析
- FFT卷积的循环特性:
fftconvolve基于FFT实现,FFT本质是循环卷积。当输入信号长度不足以覆盖线性卷积的有效范围时,循环卷积会产生混叠效应——原本应出现在正区间边缘的信号被“卷绕”到负区间,导致负区间出现非零值。 'same'模式的边界问题:使用'same'参数时,输出长度和输入一致,但FFT的循环特性会把输入的末尾与开头连接,相当于把负区间的0和正区间尾部错误地做了卷积计算。
解决方案
方法1:计算全长度卷积后截取有效范围
两个原始分布的最小值都是0,卷积结果的有效区间应为[0,2]。先计算全长度线性卷积,再截取原输入x对应的范围,同时将负区间值置0:
# 计算全长度线性卷积 full_conv = signal.fftconvolve(pdf1 / pdf1.sum(), pdf2 / pdf2.sum(), 'full') # 生成卷积结果对应的x轴范围 x_full = np.arange(2*x[0], 2*x[-1]+step, step) # 匹配原x的范围 mask = (x_full >= x[0]) & (x_full <= x[-1]) # 截取并修正负区间 res = full_conv[mask] res[x_full[mask] < 0] = 0 # 绘图 plt.scatter(x = x, y = res)
方法2:最小化补零避免混叠
不需要扩展到[-1,1],只需给输入补足够的零,让FFT循环卷积长度等于线性卷积长度(len(pdf1) + len(pdf2) - 1),这样循环卷积等价于线性卷积,不会混叠。之后截取原x对应的范围:
# 计算需要补零的长度 pad_len = len(pdf1) + len(pdf2) - 1 - len(pdf1) # 仅在尾部补零(负区间已为0) pdf1_padded = np.pad(pdf1, (0, pad_len), mode='constant') pdf2_padded = np.pad(pdf2, (0, pad_len), mode='constant') # 计算卷积 full_conv = signal.fftconvolve(pdf1_padded / pdf1_padded.sum(), pdf2_padded / pdf2_padded.sum(), 'full') # 生成对应x轴 x_full = np.arange(x[0], x[-1] + (len(pdf1_padded)-1)*step, step) # 截取并修正负区间 mask = (x_full >= x[0]) & (x_full <= x[-1]) res = full_conv[mask] res[x_full[mask] < 0] = 0 plt.scatter(x = x, y = res)
方法3:直接修正负区间值(简洁但精度有限)
如果只需要消除负区间的非零值,可直接将结果中x<0部分置0,再重新归一化:
res = signal.fftconvolve(pdf1 / pdf1.sum(), pdf2 / pdf2.sum(), 'same') # 负区间置0 res[x < 0] = 0 # 重新归一化保证概率和为1 res = res / res.sum() plt.scatter(x = x, y = res)
注意:这种方法只能修正表面问题,若混叠导致正区间结果偏差,无法解决卷积本身的计算误差,适合精度要求不高的场景。
内容的提问来源于stack exchange,提问作者matsuo_basho
相关产品推荐
相关产品推荐

