← 返回编程题库
coding-mean-excess-over-threshold-grid中等免费版2000ms未尝试

阈值网格上的均值超额曲线

Mean-Excess Curve Across a Threshold Grid

开始编码

某风控团队在 P&L 损失序列上做峰超阈值(POT)诊断,想把均值超额曲线画在一组规则的候选阈值网格上。均值超额函数

e(u)=1{i:losses[i]>u}i:losses[i]>u(losses[i]u) e(u) = \frac{1}{|\{i : \mathrm{losses}[i] > u\}|} \sum_{i : \mathrm{losses}[i] > u} (\mathrm{losses}[i] - u)

是极值理论里标准的阈值选择诊断:当 u_0 之上的超额服从形参为 xi 的广义 Pareto 分布(GPD)时,对 u >= u_0 而言 e(u) 近似为 u 的线性函数,斜率为 xi / (1 - xi)。视觉做法是在网格上评估 e(u),找到曲线开始线性的最小 u,作为 GPD 拟合的推荐阈值。

实现 solution(losses: list[float], threshold_grid: list[float]) -> list[float]:对 threshold_grid 中的每个 u(独立计算,按输入顺序),如果至少有一个损失严格大于 u 就返回经验均值超额 e(u),否则返回 float('nan')

Example

solution([1.0, 3.0, 5.0, 8.0, 12.0], [0.0, 4.0, 10.0]) 返回 [5.8, 4.333..., 2.0]

  • u = 0:所有损失都超过 0;超额量为 [1, 3, 5, 8, 12],和为 29,均值 29 / 5 = 5.8
  • u = 4:超额观测为 [5, 8, 12];超额量为 [1, 4, 8],和为 13,均值 13 / 3 ≈ 4.3333333...
  • u = 10:只有 12 超过;单一超额量为 2,均值 2.0

网格项高于最大损失时返回 nansolution([1.0, 3.0, 5.0], [10.0]) 等于 [float('nan')],因为没有损失严格大于 10。网格项与某个损失相等时不计该损失:solution([1.0, 3.0, 5.0], [3.0]) 只对 {5} 求平均(超额量为 2),得到 [2.0]——严格的 loss > u 排除了 3.0 本身。

参考解为 O((N + K) log N):把 losses 排序一次,构造长度 N + 1 的后缀和(tail_sum[j] = s[j] + ... + s[N-1]),然后对每个网格阈值 ubisect_right(s, u) 找首个超额下标 j。令 m = N - j,超额量之和为 tail_sum[j] - u * me(u) = (tail_sum[j] - u * m) / mm == 0 时输出 nan。在题目的规模下,朴素的 O(N * K) 逐阈值重扫也能在时限内完成。

实现细节由 stubs/stub.py 提供。

Edge cases

  • N == 0(空 losses):每个网格项都是 nan;返回 [float('nan')] * K
  • K == 0(空网格):返回 []。空网格优先于空 losses。
  • u 等于 max(losses):严格的 > 排除最大值,超额集合为空,输出 nan
  • u < min(losses):所有损失都计入;e(u) = mean(losses) - u
  • u 等于某个内部观测 v:等于 v 的项被排除(严格 >),只有严格大于 v 的损失构成超额集合。
  • threshold_grid 中有重复值:每个重复项独立计算,在它各自的位置上各自产生一个输出。
  • 负阈值与有正有负的损失(既有亏损也有盈利):没有特别处理,严格 > 直接作用于带符号数值。

实践背景

阈值选择是 GPD 拟合中最脆弱的一步:u 选得太低会破坏 GPD 假设,u 选得太高则超额样本太少、xi 估计精度差。均值超额图——网格上的 e(u)u 的曲线——给出一个视觉化健康检查:一段稳定的线性区表示从该最小 u 起 GPD 假设可信。风控桌面每天在新损失样本上跑这条曲线,配合 Hill 估计选定阈值,再喂到下游的尾部 VaR 与期望损失外推中。对超过经验最大值的网格项返回 nan 是刻意为之:让看板能区分「该 u 之上没有尾部数据」与「尾部平坦」,避免自动告警把空集静默吞掉。

约束条件

  • 0 <= N <= 4000,其中 N = len(losses)
  • 0 <= K <= 1000,其中 K = len(threshold_grid)
  • 每个 `losses[i]` 有限且 |losses[i]| <= 1e9(允许有符号,正值表示亏损、负值表示盈利)
  • 每个 `threshold_grid[k]` 有限且 |threshold_grid[k]| <= 1e9;网格无需有序;允许重复
  • 输出是 Python `list[float]`,长度为 K;元素是有符号的有限浮点数或 `float('nan')`;输出顺序与 `threshold_grid` 输入顺序一致
  • 浮点比较容差:rel_tol = 1e-9、abs_tol = 1e-9;本题 `nan` 与 `nan` 视为相等

样例

Case 1 · typical: small example with three thresholds

输入: [[1,3,5,8,12],[0,4,10]]

期望: [5.8,4.333333333333333,2]

u=0:所有 5 个损失都超过;超额和 1+3+5+8+12=29,均值 29/5=5.8。u=4:超额为 {5,8,12},超额和 1+4+8=13,均值 13/3。u=10:只有 12 超过,单一超额量 2。

Case 2 · boundary: threshold equal to max returns nan (strict >)

输入: [[2,5,7,9],[9]]

期望: ["NaN"]

u 等于 max(losses)=9;严格 > 排除最大值,超额集合为空,输出 nan。

最近提交

还没有提交记录。

编码区

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

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

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

Case 1 · typical: small example with three thresholds

输入: [[1,3,5,8,12],[0,4,10]]

期望: [5.8,4.333333333333333,2]

u=0:所有 5 个损失都超过;超额和 1+3+5+8+12=29,均值 29/5=5.8。u=4:超额为 {5,8,12},超额和 1+4+8=13,均值 13/3。u=10:只有 12 超过,单一超额量 2。

Case 2 · boundary: threshold equal to max returns nan (strict >)

输入: [[2,5,7,9],[9]]

期望: ["NaN"]

u 等于 max(losses)=9;严格 > 排除最大值,超额集合为空,输出 nan。