2. 生存年金现值计算

任务说明

给定一张简化生命表(各年龄的生存人数 l_x)和年利率 i,计算被保险人在年龄 x 时的两类生存年金现值:

  • 即期生存年金(期末付,a_x):每年年末在被保险人存活时给付 1 元。
  • 期初付生存年金(ä_x):每年年初在被保险人存活时给付 1 元。

核心公式:设折现因子 v = 1/(1+i),_kp_x = l_{x+k}/l_x,则

a_x = Σ_{k=1}^{ω-x} v^k · _kp_x,ä_x = Σ_{k=0}^{ω-x-1} v^k · _kp_x,且 ä_x = 1 + a_x(在整数年龄、单位给付下成立)。

实现代码

import math

# 简化生命表:年龄 -> 生存人数 l_x(0 岁 100000 人,最高年龄 5 岁)
LIFE_TABLE = {0: 100000, 1: 99000, 2: 98000, 3: 96000, 4: 92000, 5: 0}

def survival_prob(lx, lx_k):
    """k 年生存概率 _kp_x = l_{x+k} / l_x"""
    return lx_k / lx

def annuity_present_values(x, i, table):
    """计算年龄 x 的即期生存年金 a_x 与期初付生存年金 ä_x"""
    lx = table[x]
    v = 1.0 / (1.0 + i)
    omega = max(table.keys())          # 最高年龄
    a_x = 0.0
    # 即期(期末付):k = 1 .. omega - x
    for k in range(1, omega - x + 1):
        lx_k = table.get(x + k, 0)
        if lx_k == 0:
            break
        a_x += (v ** k) * survival_prob(lx, lx_k)
    # 期初付:k = 0 .. omega - x - 1
    a_due = 1.0 + a_x
    return a_x, a_due

if __name__ == "__main__":
    x, i = 0, 0.05
    a_x, a_due = annuity_present_values(x, i, LIFE_TABLE)
    print(f"年龄 x={x}, 利率 i={i}")
    print(f"即期生存年金 a_x  = {a_x:.6f}")
    print(f"期初付生存年金 ä_x = {a_due:.6f}")
    # 一致性校验:ä_x 应等于 1 + a_x
    assert math.isclose(a_due, 1.0 + a_x, rel_tol=1e-12)
    print("校验通过:ä_x = 1 + a_x")

运行与验证

直接运行脚本:python annuity.py。观察输出中 a_x 与 ä_x 的数值,并确认最后一行打印“校验通过”。

判定标准:

  • 脚本无异常退出,且 assert 未触发;
  • ä_x 与 1 + a_x 在浮点误差内相等;
  • 改变 x(如 1、2)或 i(如 0.03、0.08)后重跑,结果仍满足上述关系,且 a_x 随 i 增大而减小。

关键点

生命表用字典表示,l_5 = 0 表示 5 岁后无人存活,循环在遇到 l_{x+k}=0 时提前终止,避免无效项。折现因子 v 与生存概率 _kp_x 相乘后累加,即得现值。期初付年金比即期多出第 0 年的一次确定给付,因此 ä_x = 1 + a_x,可作为实现正确性的自检条件。