在线滑点中位数
Online Median of Slippage
开始编码执行交易后,每一笔成交都会产生一条「滑点」(slippage)数据:实际成交价相对于下单时参考价的偏差,通常以基点(bps)记录。正滑点意味着付出比预期高的价格——成本损失;负滑点则是价格改善(比如买单成交在比报价更低的位置)。把当天每一笔成交的滑点收集起来,监控台想要的不是收盘后才出一个汇总,而是 每来一笔就刷新一次 当前中位数:均值容易被几笔异常巨大的滑点拉偏,而中位数对尾部 outlier 鲁棒得多——它更能代表「典型的一笔成交在被吃掉时的真实成本」。如果中位数在盘中悄悄漂移,往往意味着对手方流动性结构在变化,需要立刻收紧或放松委托强度。
举例:solution([3.0, 1.0, 4.0, 1.0, 5.0]) 应返回 [3.0, 2.0, 3.0, 2.0, 3.0]。第一笔仅 3.0,中位数 = 3.0;第二笔加入后排序为 [1, 3],偶数个取均值 = 2.0;第三笔加入 4.0 排序为 [1, 3, 4],奇数个取中间 = 3.0;第四笔加入 1.0 排序为 [1, 1, 3, 4],偶数个 = (1+3)/2 = 2.0;第五笔加入 5.0 排序为 [1, 1, 3, 4, 5],奇数个 = 3.0。
请实现 solution(slippages: list[float]) -> list[float],参考 stubs/stub.py。slippages 是按时间顺序到达的每笔成交滑点,函数返回一个等长的 list[float],每个位置都是看完到此为止的整段前缀的中位数。中位数定义按惯例:奇数个元素取排序后的正中间那个,偶数个元素取中间两个的算术平均。空输入返回 []。
朴素做法是每来一笔就 sorted(history)[len(history) // 2],单步 O(n log n)、n 步累计 O(n² log n),在 n = 10⁴ 数据规模下会跑成约 10⁹ 量级的比较——超时。正确做法是 两个堆并行维护:一个大顶堆 lower 存「下半段较小的值」(堆顶为下半段的最大值),一个小顶堆 upper 存「上半段较大的值」(堆顶为上半段的最小值)。维持不变量 len(lower) - len(upper) ∈ {0, 1}。新值到达时先按「与下半段堆顶的大小关系」决定该塞哪一侧,再做一次重平衡(最多搬一个元素从一堆到另一堆)。读取中位数:奇数总数时直接取 lower 堆顶;偶数总数时取两堆顶的均值。Python heapq 只有小顶堆,做大顶堆把存进去的值取负即可。单步 O(log n),n 步总计 O(n log n)——这就是为什么本题被放进「堆 / 流式统计」技术簇:当需要在线维护「分位数」类指标且全排序结构是 overkill 时,配对堆是把单步成本压到对数级的标准结构。
约束条件
- 0 ≤ len(slippages) ≤ 10000
- 每个 slippages[i] 是 float,可以为正、零或负(负值表示成交价优于参考价)
- 返回 list[float],长度与输入严格相等;result 的第 i 项是看完前 i+1 笔成交后整段前缀的中位数
- 偶数长度前缀的中位数定义为中间两值的算术平均;奇数长度取正中间一个值
- 空输入返回 [],不要返回 [0.0] 或 None
- 时间复杂度需 O(n log n);O(n² log n) 的全排序解在 n = 10⁴ 数据规模会超时
样例
Case 1 · example from statement (mixed slippage values)
输入: [[3,1,4,1,5]]
期望: [3,2,3,2,3]
逐步看:第 1 笔仅 `3.0`,中位数 = 3.0;第 2 笔加入 `1.0` 后排序为 `[1, 3]`,**偶数个**取中间两值的均值 = (1+3)/2 = 2.0;第 3 笔加入 `4.0` 排序为 `[1, 3, 4]`,奇数个取正中间 = 3.0;第 4 笔加入 `1.0` 排序为 `[1, 1, 3, 4]`,偶数个 = (1+3)/2 = 2.0;第 5 笔加入 `5.0` 排序为 `[1, 1, 3, 4, 5]`,奇数个 = 3.0。注意每来一个新值都要立刻输出一次,最终列表与输入等长。
Case 2 · single fill (warm-up case)
输入: [[2.5]]
期望: [2.5]
只有一笔成交时,中位数就是这一笔本身。这个用例验证「仅一个堆里有元素」时也能正确输出——不要把 `len(lower) == len(upper) == 0` 的逻辑漏掉,也不要在还没初始化堆顶就去访问 `lower[0]` 和 `upper[0]`。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · example from statement (mixed slippage values)
输入: [[3,1,4,1,5]]
期望: [3,2,3,2,3]
逐步看:第 1 笔仅 `3.0`,中位数 = 3.0;第 2 笔加入 `1.0` 后排序为 `[1, 3]`,**偶数个**取中间两值的均值 = (1+3)/2 = 2.0;第 3 笔加入 `4.0` 排序为 `[1, 3, 4]`,奇数个取正中间 = 3.0;第 4 笔加入 `1.0` 排序为 `[1, 1, 3, 4]`,偶数个 = (1+3)/2 = 2.0;第 5 笔加入 `5.0` 排序为 `[1, 1, 3, 4, 5]`,奇数个 = 3.0。注意每来一个新值都要立刻输出一次,最终列表与输入等长。
Case 2 · single fill (warm-up case)
输入: [[2.5]]
期望: [2.5]
只有一笔成交时,中位数就是这一笔本身。这个用例验证「仅一个堆里有元素」时也能正确输出——不要把 `len(lower) == len(upper) == 0` 的逻辑漏掉,也不要在还没初始化堆顶就去访问 `lower[0]` 和 `upper[0]`。