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

在线累计 Pearson 相关系数(Welford 共动差递推)

Running Cumulative-History Pearson Correlation via Welford-Style Co-Moment Recurrence

开始编码

实现 solution(x_stream: list[float], y_stream: list[float]) -> list[float]。给定两条等长配对流 x_streamy_stream,长度 N (≥ 0),维护五个 Welford 风格运行标量 (mean_x, mean_y, M2_x, M2_y, C2_xy)——其中 M2_x = sum_i (x_i − mean_x_n)**2M2_y = sum_i (y_i − mean_y_n)**2C2_xy = sum_i (x_i − mean_x_n)(y_i − mean_y_n) 分别是关于运行均值的累计二阶中心矩与累计交叉中心矩。每观测到新的一对 (x_new, y_new) 后,输出累计历史 Pearson 相关系数

corr_k = C2_xy / sqrt(M2_x * M2_y)

仅当 M2_x > 0M2_y > 0 同时成立;否则输出 float('nan')。索引 k = 0(n = 1)时两条运行方差按构造严格为 0,因此首项始终为 NaN;任一流的前缀为常数也输出 NaN。空输入返回 []

第 (k+1) 次观测的 Welford 共动差更新(更新后 n = k + 1)为

delta_x   = x_new - mean_x
mean_x_new = mean_x + delta_x / n
delta_y   = y_new - mean_y
mean_y_new = mean_y + delta_y / n
M2_x  += delta_x * (x_new - mean_x_new)
M2_y  += delta_y * (y_new - mean_y_new)
C2_xy += delta_x * (y_new - mean_y_new)   # 不对称:旧 mean_x、新 mean_y

C2 行是唯一的微妙之处。它通过 delta_x 使用旧 mean_x,通过 y_new − mean_y_new 使用新 mean_y;代数等价对偶 delta_y * (x_new − mean_x_new) 也可。混搭错误的两半(例如 delta_x * (y_new − mean_y) 仍取旧 mean_y)会沉默地偏移轨迹。

solution([0.01, -0.02, 0.015, -0.005, 0.03], [0.02, -0.01, 0.018, -0.008, 0.025]) 大致返回 [NaN, 1.0, 0.9816036292717386, 0.9235589222731387, 0.9324346713662166]。在 k = 0 两条方差精确为 0,所以输出 NaN;在 k = 1 两点样本恰好共线,corr = 1 到机器精度(一般地:任意两点都完美共线);从 k = 2 起运行相关系数即为该前缀的样本 Pearson 相关,因为两条流大体同向运动而维持接近 1。总开销 O(N) 时间、O(N) 输出空间、O(1) 额外状态。

朴素替代——每一步从前缀重新计算 (mean(x*y) − mean(x)·mean(y)) / sqrt(var(x)·var(y))——是 O(N²) 的,并且当数据聚集在远离零的位置时会失精度(参见以 1e6 为均值的 adversarial 用例,那里两遍式得到的甚至不再夹在 [−1, 1] 内)。

函数骨架见 stubs/stub.py

实践背景

跨资产风控监视台上常常实时盯着两条收益流之间的累计历史 Pearson 相关——例如某策略每根 bar 的 PnL 流与一条基准因子流,或者两条本应作为对冲的 book-mid 收益流。这里相关系数不被当作预测,而是用作体制变化哨兵:相关系数突降意味着市场状态变了,或者策略与原本的驱动脱钩了;无论是哪一种,风控系统都希望提前几分钟而不是滞后几分钟知道。Welford 共动差递推让"每个 tick 在每对受监对上更新"足够便宜:每观测一次只更新一次、不需要回看历史、并且在台面数据日常跨越的几个数量级范围(盘中收益 tick 的 1e-4 与跨夜 P&L 的 1e6 并存)下数值稳定。生产中真正会咬人的两条合约正是测试在考的:n = 1 时的 NaN 兜底(出错则要么在第一次观测就崩溃,要么悄悄输出 0/1 而仪表盘以为已经在线),以及常数前缀的 NaN 分支(行情卡住或暂停送出恒值时面板必须显式给出"未定义",而不是 0 或除零异常)。这与 EWMA 衰减协方差不同——这里没有衰减旋钮,每次观测等权贡献——也与秩相关不同——后者根本不使用值层面的 Pearson 公式。

约束条件

  • 0 <= len(x_stream) == len(y_stream) <= 4000;每个元素为有限浮点数,绝对值不超过 1e6
  • 对每个索引 k(0 起算),第 k 个输出基于 x_stream[0..k] 与 y_stream[0..k],即 n = k + 1 对配对观测
  • 若第 k 次更新后 M2_x == 0.0 或 M2_y == 0.0,则输出 float('nan');否则输出 C2_xy / sqrt(M2_x * M2_y)
  • n = 1 时两条运行方差按构造为 0,因此 out[0] 必为 float('nan');任一流的前缀为常数都会强制将该索引置为 NaN,直至首次出现差异
  • 比对采用浮点容差 rel_tol=1e-9, abs_tol=1e-9,并启用 NaN 与 NaN 相等语义;corr 受舍入影响可能略微突破 1.0(不超过 abs_tol),所以比对器不得做截断

样例

Case 1 · statement-example: 5-step paired return stream — running corr settles near +1 as same-sign moves accumulate

输入: [[0.01,-0.02,0.015,-0.005,0.03],[0.02,-0.01,0.018,-0.008,0.025]]

期望: ["NaN",1,0.9816036292717386,0.9235589222731387,0.9324346713662166]

前两步:k=0 两条方差均为 0 输出 NaN;k=1 两点共线 corr=1。从 k=2 起相关系数即为前缀样本 Pearson 相关,并因两条流大体同向运动维持接近 1。

Case 2 · statement-example: empty inputs return the empty list

输入: [[],[]]

期望: []

空输入直接返回空列表。

Case 3 · statement-example: single pair (n=1) — running variances are 0 by construction, so the only entry is NaN

输入: [[1.5],[-2]]

期望: ["NaN"]

n=1 时 M2_x = M2_y = 0,按合约输出 NaN。

Case 4 · statement-example: two distinct pairs (n=2) — second entry is exactly +1 because two points are always co-linear

输入: [[0,1],[0,0.5]]

期望: ["NaN",1]

k=0 输出 NaN;k=1 两点必然共线,corr 等于 ±1。

最近提交

还没有提交记录。

编码区

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

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

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

Case 1 · statement-example: 5-step paired return stream — running corr settles near +1 as same-sign moves accumulate

输入: [[0.01,-0.02,0.015,-0.005,0.03],[0.02,-0.01,0.018,-0.008,0.025]]

期望: ["NaN",1,0.9816036292717386,0.9235589222731387,0.9324346713662166]

前两步:k=0 两条方差均为 0 输出 NaN;k=1 两点共线 corr=1。从 k=2 起相关系数即为前缀样本 Pearson 相关,并因两条流大体同向运动维持接近 1。

Case 2 · statement-example: empty inputs return the empty list

输入: [[],[]]

期望: []

空输入直接返回空列表。

Case 3 · statement-example: single pair (n=1) — running variances are 0 by construction, so the only entry is NaN

输入: [[1.5],[-2]]

期望: ["NaN"]

n=1 时 M2_x = M2_y = 0,按合约输出 NaN。

Case 4 · statement-example: two distinct pairs (n=2) — second entry is exactly +1 because two points are always co-linear

输入: [[0,1],[0,0.5]]

期望: ["NaN",1]

k=0 输出 NaN;k=1 两点必然共线,corr 等于 ±1。