在线运行超额峰度(Welford 四阶矩递推)
Online Running Excess Kurtosis via Welford-Style Fourth-Moment Recurrence
开始编码实现 solution(returns: list[float]) -> list[float]。给定长度为 N 的逐期收益序列,输出长度为 N 的 运行超额峰度 列表:第 i 个元素(0 索引)是 returns[0..i](含两端)即前 k = i + 1 个样本的总体(有偏)超额峰度。维护运行均值 m、二阶中心矩和 M2 = sum_j (x_j - m_k)**2、三阶中心矩和 M3 = sum_j (x_j - m_k)**3、四阶中心矩和 M4 = sum_j (x_j - m_k)**4,用 Welford 风格的在线递推更新,并在每一步输出
excess = k * M4 / (M2 * M2) - 3k < 4(即 i < 3)时输出 float('nan');k >= 4 但 M2 == 0.0(常数前缀)时同样输出 float('nan')——方差为 0 峰度无定义。即便不直接输出 M3 也必须维护它——M4 的更新含 -4 * delta_n * M3_old 交叉项,缺了 M3 通道会在所有非对称流上把 M4 算错。
例
solution([0.02, -0.01, 0.03, -0.04, 0.05, -0.10, 0.01, 0.06]) 大约返回 [NaN, NaN, NaN, -1.4266666666666665, -1.172, -0.7320728915478769, -0.2693612581362892, -0.15623530053378243]。手工演算:前 3 步 k<4 输出 NaN。k=4 时样本为 {0.02, -0.01, 0.03, -0.04},均值 0、M2 = 0.003、M3 = -3e-5、M4 = 3.54e-6,总体超额峰度 = 4 * 3.54e-6 / (0.003)**2 - 3 = 1.5733... - 3 = -1.4266...——平峰显著,因为 4 个均匀分布的点比正态分布尾部更轻。第 5 步纳入 +0.05(仍较为均匀,平峰但轻些)。第 6 步纳入深度亏损 -0.10,左尾增厚,运行超额峰度由 -1.17 跳升到 -0.73(尾部已显著变重,更接近正态)。第 7、8 步纳入中等幅值,尾部质量进一步稀释,运行超额峰度自下而上趋近 0。
第 k 步(观测到 x_k 后)的 Welford 风格更新为
delta = x_k - m_old
delta_n = delta / k
delta_n2 = delta_n * delta_n
term1 = delta * delta_n * (k - 1)
M4 += term1 * delta_n2 * (k*k - 3*k + 3) + 6 * delta_n2 * M2_old - 4 * delta_n * M3_old
M3 += term1 * delta_n * (k - 2) - 3 * delta_n * M2_old
M2 += term1
m += delta_n总开销 O(N) 时间、O(N) 输出空间,O(1) 额外状态。朴素备选——每步重新计算 mean((x-m)**4) / mean((x-m)**2)**2 - 3(或从最终均值做两遍扫描)——是 O(N^2),且当数据聚集在远离 0 处时(见以 1e6 为中心的对抗用例)会因取消而灾难性丧失精度。
函数骨架见 stubs/stub.py。
实践背景
实时分布形状监控是衍生品/系统化股票交易桌的标准面板。已实现波动率(二阶矩)告诉你策略 *动得多大*;运行偏度告诉你这些移动 *偏向哪一边*;运行超额峰度则是同一三联图的第三条腿——告诉你 *尾部正在变多重*。即便方差估计看起来温和,做市账本上若运行超额峰度长期上升,是分布正把质量集中到稀有极端波动的早期预警:下一笔超大亏损会比近期收益波动率所暗示的更大、更突然。运行偏度面板(本题库另一道兄弟题)与运行峰度面板的交叉读数,是桌面区分"左尾事件"(偏度转负、峰度上升)与"双侧制度"(偏度近 0、峰度仍上升)的标准做法。当桌面希望用整段回测、保持累积方差/偏度面板同样数值条件的稳定估计器时,累积式 Welford 递推优于 EWMA。生产中最容易踩的两个合约细节正是本题考的两个:k < 4 的 NaN 下限(缺它前三步要么除零崩溃,要么悄悄输出误导值而面板宣称已上线),以及 M2 == 0 的 NaN 分支(停盘或冻结行情会推送常数流,面板必须打出"未定义"而不是 0.0 或除零异常)。
约束条件
- 0 <= len(returns) <= 4000;每个元素为有限浮点数,绝对值不超过 1e6
- 对每个步 i(0 索引),第 i 个输出使用 returns[0..i](含两端),即 k = i + 1 个样本
- 若 k < 4 输出 float('nan');若 k >= 4 但 M2 == 0.0(常数前缀)也输出 float('nan')
- 使用总体(有偏)超额峰度 k * M4 / (M2 * M2) - 3,*不是* 样本无偏修正形式
- 每一步的更新顺序是 M4(读旧 M2 与旧 M3)-> M3(读旧 M2)-> M2 -> mean;任何两步交换都会读到陈旧状态
样例
Case 1 · statement-example: 8-step return stream — running excess kurtosis sharpens after a deep loss enters
输入: [[0.02,-0.01,0.03,-0.04,0.05,-0.1,0.01,0.06]]
期望: ["NaN","NaN","NaN",-1.4266666666666665,-1.172,-0.7320728915478769,-0.2693612581362892,-0.15623530053378243]
前 3 个 (k<4) 输出 NaN;k=4 起返回峰度估计;当 -0.10 这一较大负收益进入后,分布尾部变重,超额峰度从负向 0 靠拢。
Case 2 · statement-example: empty stream returns empty list
输入: [[]]
期望: []
空输入直接返回空列表,无需进入更新循环。
Case 3 · statement-example: single element — k=1 emits NaN
输入: [[0.05]]
期望: ["NaN"]
k=1 不足以定义峰度,输出 NaN。
Case 4 · statement-example: three elements — all NaN (k<4)
输入: [[0.01,-0.02,0.03]]
期望: ["NaN","NaN","NaN"]
k=1、2、3 都不足以计算超额峰度(需要 k>=4),三个位置全部为 NaN。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: 8-step return stream — running excess kurtosis sharpens after a deep loss enters
输入: [[0.02,-0.01,0.03,-0.04,0.05,-0.1,0.01,0.06]]
期望: ["NaN","NaN","NaN",-1.4266666666666665,-1.172,-0.7320728915478769,-0.2693612581362892,-0.15623530053378243]
前 3 个 (k<4) 输出 NaN;k=4 起返回峰度估计;当 -0.10 这一较大负收益进入后,分布尾部变重,超额峰度从负向 0 靠拢。
Case 2 · statement-example: empty stream returns empty list
输入: [[]]
期望: []
空输入直接返回空列表,无需进入更新循环。
Case 3 · statement-example: single element — k=1 emits NaN
输入: [[0.05]]
期望: ["NaN"]
k=1 不足以定义峰度,输出 NaN。
Case 4 · statement-example: three elements — all NaN (k<4)
输入: [[0.01,-0.02,0.03]]
期望: ["NaN","NaN","NaN"]
k=1、2、3 都不足以计算超额峰度(需要 k>=4),三个位置全部为 NaN。