为截断正态分布曲线添加满足特定约束的尾部
需求可行性确认与解决方案
可行性结论
该需求完全可行,我们可以通过解析推导的截断正态尾部模型实现,全程无迭代搜索,计算效率极高。
核心设计思路
为左右尾部分别拟合以原曲线端点为峰值的正态分布延伸段,通过数学推导直接求解参数,满足以下约束:
- 尾部与原曲线端点函数值完全匹配
- 左右尾部积分和等于指定值
- 左右尾部加权积分和等于指定值
我们将左尾部设计为以左端点(x1,y1)为峰值的正态分布左半段(x ≤ x1时单调递增至y1),右尾部设计为以右端点(x2,y2)为峰值的正态分布右半段(x ≥ x2时单调递减至y2),通过这种简化可以将参数求解转化为一元二次方程的解析解,避免任何迭代计算。
数学推导(简化版)
尾部PDF定义
左尾部PDF:f_L(x) = A_L · φ((x-x1)/σ_L),其中φ为标准正态PDF,且f_L(x1)=y1,推导得A_L = y1·√(2π)
右尾部PDF:f_R(x) = A_R · φ((x-x2)/σ_R),同理得A_R = y2·√(2π)积分与加权积分约束
设左尾部积分I_L、右尾部积分I_R,满足I_L + I_R = integral
设左尾部加权积分W_L、右尾部加权积分W_R,满足W_L + W_R = weighted_integral通过变量替换与积分化简,最终可将约束转化为关于
u = A_L·σ_L的一元二次方程,直接求解得到u后,即可推导出σ_L、σ_R等所有参数。
代码实现
import numpy as np def add_tails(lower_intercept, upper_intercept, integral, weighted_integral): x1, y1 = lower_intercept x2, y2 = upper_intercept sqrt_2pi = np.sqrt(2 * np.pi) A_L = y1 * sqrt_2pi A_R = y2 * sqrt_2pi # 构造一元二次方程系数:a*u² + b*u + c = 0 a = -1/(y1 * 2 * np.pi) - 1/(y2 * 2 * np.pi) b = 0.5 * x1 + 0.5 * x2 + (2 * integral)/(y2 * 2 * np.pi) c = -weighted_integral - 0.5 * x2 * 2 * integral + ((2 * integral)**2)/(y2 * 2 * np.pi) # 求解方程 discriminant = b**2 - 4 * a * c if discriminant < 0: raise ValueError("输入参数无解,请检查积分与加权积分的合理性") # 筛选符合物理意义的解(积分必须为正) candidates = [(-b + np.sqrt(discriminant))/(2*a), (-b - np.sqrt(discriminant))/(2*a)] u = None for candidate in candidates: v = 2 * integral - candidate if candidate > 0 and v > 0: u = candidate break if u is None: raise ValueError("无法生成正面积的尾部,请调整参数") sigma_L = u / A_L sigma_R = (2 * integral - u) / A_R # 返回尾部PDF函数 def tail_left(x): z = (x - x1) / sigma_L return A_L * np.exp(-0.5 * z**2) / sqrt_2pi def tail_right(x): z = (x - x2) / sigma_R return A_R * np.exp(-0.5 * z**2) / sqrt_2pi return tail_left, tail_right
使用示例与验证
# 调用示例 tail_left, tail_right = add_tails( lower_intercept=[4780, 0.015], upper_intercept=[4805, 0.01], integral=0.1, weighted_integral=4800 ) # 验证端点匹配 print(tail_left(4780)) # 输出≈0.015 print(tail_right(4805)) # 输出≈0.01 # 验证积分与加权积分(需导入scipy) from scipy.integrate import quad total_integral = quad(tail_left, -np.inf, 4780)[0] + quad(tail_right, 4805, np.inf)[0] total_weighted = quad(lambda x: x*tail_left(x), -np.inf,4780)[0] + quad(lambda x:x*tail_right(x),4805,np.inf)[0] print(f"尾部总积分:{total_integral:.4f}") # 输出≈0.1 print(f"尾部加权积分:{total_weighted:.1f}") # 输出≈4800
内容的提问来源于stack exchange,提问作者SSC Fan
相关产品推荐
相关产品推荐

