等距现金流的周期 IRR:标准项目假设下的二分求根
Periodic IRR of an Equally-Spaced Cashflow Stream Under the Standard-Project Assumption via Bisection
开始编码实现 solution(cashflows: list[float]) -> float。给定一组等距现金流 cashflows:cashflows[0] 在 t = 0,cashflows[i] 在 t = i(同一个固定周期长度)。返回**周期 IRR r**,即满足 NPV(r) = sum_{i=0}^{N-1} cashflows[i] / (1 + r)**i = 0 的实数。本题只接受标准项目形态:cashflows[0] 严格为负(初始投入),后续至少有一项严格为正,且整段非零子序列恰好出现一次符号翻转——这是经典的「唯一 IRR > -1」充分条件。任何违反此前提的输入(N < 2、空、全零、零次或多于一次符号翻转、cashflows[0] >= 0 等)一律返回 float("nan")。多 IRR 的歧义不在本题范围内。
例
solution([-100.0, 60.0, 60.0]) 返回约 0.13066238629。NPV(0.13066238629) = -100 + 60/1.13066... + 60/1.13066...**2 ≈ 0;前置费用 100、两期各回收 60,正好对应一个内部收益率约 13.07%/期。solution([-1.0, 0.5, 0.5, -0.3]) 返回 float("nan"):尽管 cashflows[0] < 0 且后续有正值,但 -1 → +0.5(翻转 1 次)、+0.5 → -0.3(翻转 2 次)合计两次符号翻转,不再满足唯一性条件,落入多 IRR 歧义区。solution([-1.0, 0.0, 0.0, 2.0]) 返回约 0.25992104989:中间两个 0 被跳过,非零子序列是 [-1, 2],仅一次翻转——这是合规的标准项目。
关键形态是**在 r > -1 上对答案二分**:由 Descartes 符号规则(令 x = 1/(1+r) > 0,多项式系数序列恰好一次符号翻转),NPV(r) 在 r > -1 上至多有一个实根;又 NPV(r → -1+) → +∞、NPV(+∞) → cashflows[0] < 0,因此根存在且唯一,端点异号——即便 NPV 全局非单调,二分依然能稳定收敛到该唯一根。先做「标准项目」预筛选(恰好一次符号翻转),不合规直接返回 float("nan");否则自适应地确定二分初始区间——例如 lo = -0.5 起、若 NPV(lo) 不为正则把 lo 往 -1 推半步、hi = 1 起、若 NPV(hi) 不为负则把 hi 翻倍——再对该区间二分至 hi - lo < 1e-9(远低于比较器 abs_tol = 1e-7)即停。在 r 上做细步长网格扫描的成本是 O(R / step * N),在输入上界会 TLE。
函数骨架见 stubs/stub.py。
实践背景
固收 / 项目融资 / 撮合交易后估值常需对一个期限固定、票面已知的现金流序列计算 IRR:例如一笔零息债券加权或一段久期内的固定票息打包,或一只资产支持票据的预测现金流——给定初始投入和每个等距时点的回款额,求让 NPV 归零的周期收益率。它是债券估值与项目可行性两条主线最朴素的入口指标,没有闭式解,工业界一律用迭代法。本题刻意把范围限定在「标准项目」上:单次符号翻转保证唯一 IRR 存在,可以稳定二分;现实中遇到多次符号翻转的现金流(例如再投资期 + 退役成本期重新转负)需要切换成 MIRR 或返回所有实根,这超出本题范围,所以预筛选不合规输入并返回 NaN。由于 |cashflows[i]| <= 1e9 允许的 IRR 跨度极大(例如 [-1, 1e9] 的 IRR 接近 1e9 - 1),所以参考解必须自适应地扩张二分区间——直接固定 (lo, hi) 在某些极端输入上会得不到合法的两端异号区间。
约束条件
- 2 <= N <= 5000,其中 N = len(cashflows) 是等距现金流的期数;cashflows[i] 为有限实数,|cashflows[i]| <= 1e9
- 标准项目前提:cashflows[0] < 0、至少存在一个 cashflows[i] > 0(i >= 1)、整段非零子序列**恰好一次**符号翻转(中间为 0 的项不计入翻转)
- 若违反前提(N < 2、cashflows[0] >= 0、全部非负、零次翻转、多于一次翻转、全 0 等任意一种)一律返回 float('nan');否则返回唯一满足 `NPV(r) = 0` 的周期 IRR `r`(必然 `r > -1`,但其大小不在固定区间内——需要按输入自适应地确定二分上下界)
- 输出为单个 float;比较器为 float,rel_tol = 1e-6、abs_tol = 1e-7,并启用 nan_equals_nan = true(NaN 是合约的一部分,不是错误)
- 参考解复杂度:O(N * (log(initial_bracket / 1e-9) + bracket_expansion)),内层 NPV 评估为 O(N)
样例
Case 1 · statement-example: simple two-period 60+60 outlay 100
输入: [[-100,60,60]]
期望: 0.13066238642204553
-100 投入,两期各回收 60,IRR ≈ 13.07%/期
Case 2 · statement-example: multi-sign-change returns NaN
输入: [[-1,0.5,0.5,-0.3]]
期望: "NaN"
非零子序列 -1 → +0.5 → -0.3 共两次符号翻转,多 IRR 歧义返回 NaN
Case 3 · statement-example: interior zeros do not count as flips
输入: [[-1,0,0,2]]
期望: 0.25992105004843324
中间两个 0 跳过,非零子序列 [-1, 2] 一次翻转,合规标准项目
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: simple two-period 60+60 outlay 100
输入: [[-100,60,60]]
期望: 0.13066238642204553
-100 投入,两期各回收 60,IRR ≈ 13.07%/期
Case 2 · statement-example: multi-sign-change returns NaN
输入: [[-1,0.5,0.5,-0.3]]
期望: "NaN"
非零子序列 -1 → +0.5 → -0.3 共两次符号翻转,多 IRR 歧义返回 NaN
Case 3 · statement-example: interior zeros do not count as flips
输入: [[-1,0,0,2]]
期望: 0.25992105004843324
中间两个 0 跳过,非零子序列 [-1, 2] 一次翻转,合规标准项目