← 返回编程题库
coding-online-running-skewness中等免费版2000ms未尝试

在线运行样本偏度(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.5

n < 3(即 i < 2)时输出 float('nan')n >= 3M2 == 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 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

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。