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

在线流式计算均值、方差、偏度与超额峰度(Welford-Pebay)

Online Streaming Mean, Variance, Skewness, and Excess Kurtosis (Welford-Pebay)

开始编码

某流式风险监控持续接收 P&L(或收益) tick,每来一个就要把*累计*的样本均值样本方差样本偏度样本超额峰度算出来——而且不能存历史。偏度与超额峰度是衡量分布对称性与厚尾的标准工具:盘中盯实时 P&L 的桌台不仅关心已实现波动率是否在抬升,更关心分布是否开始非对称(偏度向负漂——亏损端变厚)或者开始重尾(超额峰度跳升——尾部事件变频繁)。一个 7×24 的监控显然不能存下每一笔 tick;标准做法是 Welford 在线算法的高阶推广,即 Pébay (2008) 更新——只维护五个标量 (n, M1, M2, M3, M4),每个样本 O(1) 完成更新。

实现 solution(values: list[float]) -> dict。逐个处理 values(不能缓存整个数组、不能做第二遍扫描),最后返回四个样本统计量:

variance=M2n1,skewness=M3/n(M2/n)3n2(n1)(n2), \text{variance} = \frac{M_2}{n - 1}, \qquad \text{skewness} = \frac{M_3 / n}{\sqrt{(M_2 / n)^3}} \cdot \frac{n^2}{(n - 1)(n - 2)},

kurtosis=n1(n2)(n3)((n+1)nM4M223(n1)), \text{kurtosis} = \frac{n - 1}{(n - 2)(n - 3)} \left( \frac{(n + 1)\, n\, M_4}{M_2^2} - 3(n - 1) \right),

其中 Mk=i=1n(xixˉ)kM_k = \sum_{i=1}^{n}(x_i - \bar x)^k 是 Pébay 更新维护的滚动中心矩累加量:

Δ=xM1,δ=Δn+1,t=Δδn, \Delta = x - M_1, \quad \delta = \tfrac{\Delta}{n+1}, \quad t = \Delta\,\delta\,n,

M4+=tδ2((n+1)23(n+1)+3)+6δ2M24δM3, M_4 \mathrel{+}= t\,\delta^{2}\bigl((n+1)^2 - 3(n+1) + 3\bigr) + 6\,\delta^{2}\,M_2 - 4\,\delta\,M_3,

M3+=tδ(n1)3δM2,M2+=t,M1+=δ. M_3 \mathrel{+}= t\,\delta\,(n - 1) - 3\,\delta\,M_2, \qquad M_2 \mathrel{+}= t, \qquad M_1 \mathrel{+}= \delta.

更新顺序很关键:M4M_4 用的是旧的 M2M_2M3M_3M3M_3 用的是旧的 M2M_2;最后才能改写 M2M_2M1M_1。返回的 kurtosis超额峰度(已减 3,正态样本期望 ≈ 0)。

边界情形:

  • n == 0:返回 {"n": 0, "mean": 0.0, "variance": 0.0, "skewness": 0.0, "kurtosis": 0.0}
  • n == 1:方差、偏度、峰度均为 0.0(样本量不足)。
  • n == 2:方差有定义,但偏度、峰度为 0.0
  • n == 3:方差、偏度有定义,但峰度为 0.0(需 n >= 4)。
  • 所有元素相等:M2 = 0,方差、偏度、峰度全部为 0.0mean 仍如实返回该常数。

例如,solution([1.0, 2.0, 4.0]) 返回 {"n": 3, "mean": 2.333..., "variance": 2.333..., "skewness": 1.7181079837227284, "kurtosis": 0.0}(均值 7/3、样本方差 7/3、n<4 所以峰度强制为 0)。solution([3.5, 3.5, 3.5, 3.5, 3.5]) 返回 {"n": 5, "mean": 3.5, "variance": 0.0, "skewness": 0.0, "kurtosis": 0.0}——所有元素相等的退化路径。solution([1.0, 1.0, 1.0, 1.0, 10.0])(极端正偏的五元素流)返回 {"n": 5, "mean": 2.8, "variance": 16.2, "skewness": 3.125, "kurtosis": 5.0}

实践背景

一个真实的 P&L 实时监控不可能把多策略账簿连续多年的每一笔交易、每一根分钟收益都留在内存里;即便只盯一个策略的逐秒 tick 流,几天之内内存就会爆掉。Welford 1962 年的在线公式从此成为流式均值与方差的教科书答案,而 Pébay (2008) 又把它自然延伸到三阶、四阶中心矩——刚好就是尾部风险面板需要的两个量。同样的五标量状态(nM1M2M3M4)天然支持成对合并(Chan–Golub–LeVeque 合并公式),所以 Spark 的 org.apache.spark.util.StatCounter、Pandas 的 rolling().skew() / rolling().kurt() 内核背后都是同一套递推。能够在尾部事件真正发生之前就识别出已实现偏度向负漂、或者超额峰度抬升的 regime,正是这套算法的目的;亚微秒级的逐 tick 预算则是逼出流式形式的根本约束。n < 4 时各项坍缩为 0 也是工程意义上的:刚启动的策略只有几笔成交,*不应当*基于三个点报出离谱的偏度/峰度;按惯例返回 0.0 就是标准的"样本不足,请忽略"信号。

约束条件

  • 0 ≤ N ≤ 10^5,其中 `N = len(values)`
  • `values` 是有限浮点列表(无 NaN、无 inf)
  • 样本方差使用 Bessel 校正(分母 `n - 1`,即 ddof=1)
  • 偏度使用 `(M3 / n) / sqrt((M2 / n)^3) * n^2 / ((n - 1)(n - 2))`
  • 返回的峰度是**超额**峰度(已减 3,正态样本期望 ≈ 0)
  • 实现必须**真流式**:逐个处理样本,只累加 `(n, M1, M2, M3, M4)` 五个标量——不要缓存整个数组
  • 返回 `dict`,键为 `n`(int)、`mean`(float)、`variance`(float)、`skewness`(float)、`kurtosis`(float);浮点比较容差 `rel_tol = 1e-9`、`abs_tol = 1e-12`

样例

Case 1 · statement-example: n=3 small list

输入: [[1,2,4]]

期望: {"n":3,"mean":2.3333333333333335,"variance":2.3333333333333335,"skewness":1.7181079837227284,"kurtosis":0}

n=3:mean=7/3、样本方差=7/3;按规范的偏度公式 (M3/n)/sqrt((M2/n)^3) * n^2/((n-1)(n-2)) 计算偏度,n<4 时超额峰度强制为 0。

Case 2 · statement-example: all-equal degenerate

输入: [[3.5,3.5,3.5,3.5,3.5]]

期望: {"n":5,"mean":3.5,"variance":0,"skewness":0,"kurtosis":0}

所有元素相等:M2=0,样本方差=0,按惯例偏度与超额峰度均返回 0;mean 仍为该常数 3.5。

Case 3 · statement-example: heavily right-skewed n=5

输入: [[1,1,1,1,10]]

期望: {"n":5,"mean":2.8,"variance":16.2,"skewness":3.1250000000000004,"kurtosis":5}

一个极端正偏样本:四个 1 与一个 10;Pebay 累加给出 M2=64.8、M3=233.28、M4=1259.712;带入公式得到样本方差 16.2、偏度 25/8=3.125、超额峰度 5.0。

Case 4 · boundary: empty list n=0

输入: [[]]

期望: {"n":0,"mean":0,"variance":0,"skewness":0,"kurtosis":0}

Case 5 · boundary: single element n=1

输入: [[42]]

期望: {"n":1,"mean":42,"variance":0,"skewness":0,"kurtosis":0}

最近提交

还没有提交记录。

编码区

实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。

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

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

Case 1 · statement-example: n=3 small list

输入: [[1,2,4]]

期望: {"n":3,"mean":2.3333333333333335,"variance":2.3333333333333335,"skewness":1.7181079837227284,"kurtosis":0}

n=3:mean=7/3、样本方差=7/3;按规范的偏度公式 (M3/n)/sqrt((M2/n)^3) * n^2/((n-1)(n-2)) 计算偏度,n<4 时超额峰度强制为 0。

Case 2 · statement-example: all-equal degenerate

输入: [[3.5,3.5,3.5,3.5,3.5]]

期望: {"n":5,"mean":3.5,"variance":0,"skewness":0,"kurtosis":0}

所有元素相等:M2=0,样本方差=0,按惯例偏度与超额峰度均返回 0;mean 仍为该常数 3.5。

Case 3 · statement-example: heavily right-skewed n=5

输入: [[1,1,1,1,10]]

期望: {"n":5,"mean":2.8,"variance":16.2,"skewness":3.1250000000000004,"kurtosis":5}

一个极端正偏样本:四个 1 与一个 10;Pebay 累加给出 M2=64.8、M3=233.28、M4=1259.712;带入公式得到样本方差 16.2、偏度 25/8=3.125、超额峰度 5.0。

Case 4 · boundary: empty list n=0

输入: [[]]

期望: {"n":0,"mean":0,"variance":0,"skewness":0,"kurtosis":0}

Case 5 · boundary: single element n=1

输入: [[42]]

期望: {"n":1,"mean":42,"variance":0,"skewness":0,"kurtosis":0}