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,可作为实现正确性的自检条件。