Minimum Tickers for Liquidity Coverage
Minimum Tickers for Liquidity Coverage
开始编码组合工程师要做一份流动性覆盖计划:给定所选 universe 中每只股票的日均成交额 liquidities,需要计算**用最少多少只股票(按流动性 top-k 选取)就能累计覆盖目标成交量 target**(例如足以在单日内清掉整个组合的成交额)。输出本身是一个结构性指标:告诉风控可执行组合的集中度有多高,也是「desk 真正需要多少只 ticker」这个 universe 修剪问题的入口。
实现 solution(liquidities: list[int], target: int) -> int。返回最小的 k,使前 k 个流动性最高的 ticker 之和 ≥ target。如果即便选满全部仍不够,返回 -1。
方法是对前缀和数组的 binary-search-on-answer:
- 把
liquidities按非递增排序,构造前缀和 ,其中 是前 大流动性之和()。 - 谓词
feasible(k) = S[k] >= target关于 单调不降——因为每个流动性都非负,多加一只 ticker 不会让覆盖变小。 - 在 上对最小可行 二分。边界:
target <= 0直接返回0(空前缀已覆盖);S[N] < target返回-1(不可行)。
示例
solution([100, 80, 50, 30, 10], 200) 返回 3。降序后已是 [100, 80, 50, 30, 10],前缀和 [0, 100, 180, 230, 260, 270]。target = 200 时 ,,故最小 ——desk 需要按流动性排名前 3 的 ticker 来覆盖目标。
solution([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 19) 返回 2。输入是升序,求解器内部排成降序 [10, 9, 8, ..., 1],前两个累加 恰好达标,。
solution([3, 1, 4, 1, 5, 9, 2, 6], 32) 返回 -1。总流动性为 3+1+4+1+5+9+2+6 = 31 < 32,把所有 ticker 都选上仍差 1,结构上不可行。对照同样输入 target = 31 时答案为 8:每只都得选,但确实可达。
参见 stubs/stub.py 的函数骨架。
实战背景
「按流动性 top-k 覆盖」是组合构建与执行台反复出现的 sizing 问题:*组合在最具流动性的几只名字里集中度多高?*、*要清掉当日预期成交额,universe 中至少要保留几只 ticker?*、*指数 ADV 的多少落在头部 10% 的成分股里?* 模式都是「让 top-k 之和达到阈值的最小 k」,同样形态出现在 sizing 对冲篮子(覆盖父组合 beta 的最小成分子集)、修剪 watchlist(其已实现波动率方差能解释组合 95% 风险的最小 k)、构建备援行情接入(冗余覆盖 universe 的最小数据商分层)等场景。每一个都是「在整数索引上、对单调 谓词、在有界答案空间内」的 binary-search-on-answer 实例——答案空间是计数而非连续参数。我们把它写成 binary-search 题(而非一行的前缀和扫描),是因为同一骨架——排序、构造关于 单调的谓词、bisect——可直接迁移到 feasible(k) 评估为 工作量的场景(蒙卡路径计数、CVaR 模拟、保证金重算),那里 bisection 的 探测次数就是实时风控和陈旧快照的差别。本题归到 binary-search-on-answer cell,是因为 bisection 跑在答案空间(整数 ticker 数 )上,而非输入数组——内层谓词是前缀和查表 S[k] >= target。
约束条件
- 0 <= len(liquidities) <= 10^5
- 0 <= liquidities[i] <= 10^9
- 0 <= target <= 10^14
- liquidities 输入顺序任意,求解器内部排序为降序
- target = 0 返回 0(空选集即覆盖空目标)
- 空列表且 target > 0,或全和 < target,返回 -1(不可行)
样例
Case 1 · empty list, target zero — zero tickers suffice
输入: [[],0]
期望: 0
target=0 时空前缀和已经满足约束,无需任何 ticker,返回 0。这是 binary-search-on-answer 的下边界 k=0 直接命中可行的情形。
Case 2 · empty list, positive target — infeasible
输入: [[],1]
期望: -1
没有任何 ticker 可选,但 target>0,不可能覆盖,返回 -1。`prefix[N]=0 < target` 即触发 infeasibility 短路。
Case 3 · classic top-3 of a sorted-descending list
输入: [[100,80,50,30,10],200]
期望: 3
降序前缀和 [0, 100, 180, 230, 260, 270]。target=200 时,prefix[2]=180<200,prefix[3]=230>=200,故最小 k=3。bisection 在 [1, 5] 上对答案空间二分,O(log N) 步即可定位。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · empty list, target zero — zero tickers suffice
输入: [[],0]
期望: 0
target=0 时空前缀和已经满足约束,无需任何 ticker,返回 0。这是 binary-search-on-answer 的下边界 k=0 直接命中可行的情形。
Case 2 · empty list, positive target — infeasible
输入: [[],1]
期望: -1
没有任何 ticker 可选,但 target>0,不可能覆盖,返回 -1。`prefix[N]=0 < target` 即触发 infeasibility 短路。
Case 3 · classic top-3 of a sorted-descending list
输入: [[100,80,50,30,10],200]
期望: 3
降序前缀和 [0, 100, 180, 230, 260, 270]。target=200 时,prefix[2]=180<200,prefix[3]=230>=200,故最小 k=3。bisection 在 [1, 5] 上对答案空间二分,O(log N) 步即可定位。