在线运行样本偏度(Welford 三阶矩递推)
Online Running Sample Skewness via Welford-Style Third-Moment Recurrence
开始编码实现 solution(returns: list[float]) -> list[float]。给定长度为 N 的逐期收益序列,输出长度为 N 的 运行样本偏度 列表:第 i 个元素(0 索引)是 returns[0..i](含两端)即前 n = i + 1 个样本的无偏修正 Fisher-Pearson 偏度 g1。维护运行均值 m、二阶中心矩和 M2 = sum_k (x_k - m_n)**2、三阶中心矩和 M3 = sum_k (x_k - m_n)**3,用 Welford 风格的在线递推更新,并在每一步输出
g1 = sqrt(n*(n-1)) / (n-2) * (M3 / n) / (M2 / n)**1.5n < 3(即 i < 2)时输出 float('nan');n >= 3 但 M2 == 0.0(常数前缀)时同样输出 float('nan')——方差为 0 偏度无定义。
例:solution([0.01, -0.02, 0.03, -0.05, 0.04, -0.10, 0.02, 0.05]) 大约返回 [NaN, NaN, -0.585582726281388, -0.32069970845481066, -0.6073931164348767, -0.7651962372943842, -1.0430692311887222, -1.0931137588232904]。手工演算:n=3 时样本为 {0.01, -0.02, 0.03},均值 0.0067、M2 ≈ 0.001267、M3 ≈ -7.41e-6,g1 = sqrt(6)/1 * (M3/3)/(M2/3)**1.5 ≈ -0.5856。后续依次纳入 -0.05(轻度左尾)、+0.04(向 0 平衡)、-0.10(更重的左尾,使运行偏度更强烈地偏负)等——左尾观测累积时运行偏度持续向负方向移动。
第 n 步(观测到 x_n 后)的 Welford 风格更新为
delta = x_n - m_old
delta_n = delta / n
term = delta * delta_n * (n - 1)
M3 += term * delta_n * (n - 2) - 3 * delta_n * M2_old # M2_old,下一行之前
M2 += term
m += delta_n总开销 O(N) 时间、O(N) 输出空间,O(1) 额外状态。朴素备选——每步重新计算 mean(x**3) - 3*mean*var - mean**3(或从最终均值做两遍扫描)——是 O(N^2),且当数据聚集在远离 0 处时(见以 1e9 为中心的对抗用例)会因取消而灾难性丧失精度。
函数骨架见 stubs/stub.py。
实践背景
实时分布形状监控是衍生品/系统化股票交易桌的标准面板。已实现波动率(二阶矩)告诉你策略 *动得多大*;运行偏度告诉你这些移动 *偏向哪一边*。做市账本上若运行偏度长期为负,是“重亏损日相对于盈利日在累积”(左尾增厚)的早期预警,往往出现在方差估计反应过来之前。在制度切换之后运行偏度突跳,是教科书式的“调整风险阈值或拉宽报价”信号。Welford 风格的递推让面板足够便宜,可以对每一档每一笔 tick 都更新——每个观测增量 O(1),无需对历史做第二遍扫描,并且对桌面端常见的数量级跨度(盘中报价变化 ~1e-4,日终 P&L ~1e6)保持数值稳定。生产中最容易踩的两个合约细节正是本题考的两个:n < 3 的 NaN 下限(这里出 bug 要么在第一笔观测就崩,要么悄悄输出误导值而桌面以为面板已上线),以及 M2 == 0 的 NaN 分支(停盘或行情冻结会推送常数流,面板必须打出“未定义”而不是 0.0 或除零异常)。
约束条件
- 0 <= len(returns) <= 2000;每个元素为有限浮点数,绝对值不超过 1e9
- 对每个步 i(0 索引),第 i 个输出使用 returns[0..i](含两端),即 n = i + 1 个样本
- 若 n < 3 输出 float('nan');若 n >= 3 但 M2 == 0.0(常数前缀)也输出 float('nan')
- 使用无偏修正样本偏度 g1 = sqrt(n*(n-1))/(n-2) * (M3/n) / (M2/n)**1.5,*不是* 总体公式 (M3/n)/(M2/n)**1.5
- 每一步必须先更新 M3 再更新 M2(M3 += delta * delta_n * (n-1) * delta_n * (n-2) - 3 * delta_n * M2_old;然后 M2 += delta * delta_n * (n-1))
样例
Case 1 · statement-example: 8-step return stream — running skewness flips sign as a tail develops
输入: [[0.01,-0.02,0.03,-0.05,0.04,-0.1,0.02,0.05]]
期望: ["NaN","NaN",-0.585582726281388,-0.32069970845481066,-0.6073931164348767,-0.7651962372943842,-1.0430692311887222,-1.0931137588232904]
前两步 (n<3) 输出 NaN;从 n=3 起返回偏度估计;随着 -0.10 这一较大负收益进入,分布向左偏移,偏度更负。
Case 2 · statement-example: empty stream returns empty list
输入: [[]]
期望: []
空输入直接返回空列表,无需进入更新循环。
Case 3 · statement-example: single element — n=1 emits NaN
输入: [[0.05]]
期望: ["NaN"]
n=1 不足以定义偏度,输出 NaN。
Case 4 · statement-example: two elements — both indices emit NaN (n<3)
输入: [[0.01,-0.02]]
期望: ["NaN","NaN"]
n=1 与 n=2 都不足以计算样本偏度,两个位置都是 NaN。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: 8-step return stream — running skewness flips sign as a tail develops
输入: [[0.01,-0.02,0.03,-0.05,0.04,-0.1,0.02,0.05]]
期望: ["NaN","NaN",-0.585582726281388,-0.32069970845481066,-0.6073931164348767,-0.7651962372943842,-1.0430692311887222,-1.0931137588232904]
前两步 (n<3) 输出 NaN;从 n=3 起返回偏度估计;随着 -0.10 这一较大负收益进入,分布向左偏移,偏度更负。
Case 2 · statement-example: empty stream returns empty list
输入: [[]]
期望: []
空输入直接返回空列表,无需进入更新循环。
Case 3 · statement-example: single element — n=1 emits NaN
输入: [[0.05]]
期望: ["NaN"]
n=1 不足以定义偏度,输出 NaN。
Case 4 · statement-example: two elements — both indices emit NaN (n<3)
输入: [[0.01,-0.02]]
期望: ["NaN","NaN"]
n=1 与 n=2 都不足以计算样本偏度,两个位置都是 NaN。