2020年深圳杯数学建模A题关于国家“先行示范区”建设中的医疗和养老保障问题解题全过程论文及程序

2020年深圳杯数学建模

A题 关于国家"先行示范区"建设中的医疗和养老保障问题

原题再现:

  2019年8月18日,《中共中央 国务院关于支持深圳建设中国特色社会主义先行示范区的意见》发布。《意见》提出了将深圳建设成为高质量发展高地、法治城市示范、城市文明典范、民生幸福标杆、可持续发展先锋的战略定位。
  按照《意见》建设"先行示范区"的发展目标,建设"民生幸福标杆"和"可持续发展先锋"是深圳城市发展密切相关的重要内容。民生幸福就要构建优质均衡的公共服务体系,建成全覆盖可持续的社会保障体系,实现幼有善育、学有优教、劳有厚得、病有良医、老有颐养、住有宜居、弱有众扶。没有优质的社会保障体系,就没有健康,也就没有民生幸福。深圳是一个快速发展的新兴城市,其人口结构、民生需求和社会环境等都与其他城市存在一定的差别,同时也出现了一些城市资源的配置、社会保障、民生健康等方面的问题。那么深圳的城市资源应该如何更合理地配置,如何建立可持续发展的社会、医疗和养老保障体系,才能满足迅速发展和变迁的城市需要,从而有利于实现建设国家"先行示范区"的发展目标。
任务:
  (1)参考国际上先进标准,根据国情和现状给出未来5年、10年和15年深圳医疗和养老保障需要实现的目标的量化描述。
  (2)根据深圳市的现状(人口数量与结构、经济收入与消费水平、医疗资源与水平、社会保障制度与能力等),分析研究在未来5年、10年和15年中,怎样合理配置医疗和养老资源(医院、保健院、养老院、医生、服务保障人员等),才能达到(1)中提出的目标。
  (3)研究设计与(1)中目标相匹配的医疗和养老保险方案。

整体求解过程概述(摘要)

  针对问题一,为了将"国际先进标准"与深圳人口年轻、流动性强、医疗需求增长快的城市特征相统一,本文首先建立四年龄组离散人口动力学模型,并将2020年人口普查作为基准状态,将2021---2025年公开统计数据仅用于后验检验。模型预测深圳常住人口在2025年、2030年和2035年分别约为1809.49万、1848.22万和1871.86万,65岁及以上人口占比分别达到6.14%、10.40%和14.48%。鉴于单一国际均值不能直接等同于本地目标,本文引入"本地规划下限---国际标杆上限---人口需求修正"的分层标杆融合机制,构建由医疗床位、医师、护士、全科医生、养老床位、照护人员、医保覆盖、长护险覆盖、社区照护和老年健康管理组成的十维目标矩阵。由此得到2025、2030、2035年综合保障指数分别为69.06、86.27和100.00,形成可量化、可阶段验收的目标体系。
  针对问题二,基于问题一得到的人口规模、老龄结构和目标矩阵,本文不再重复数据清洗,而是进一步构建"需求映射---多期资源优化---机构等价转化"的递进模型。首先将人均指标映射为床位和人员存量需求;继而以折现后的全生命周期投入、跨期建设波动和超前闲置为目标函数,以目标下界、存量单调性、护医比和照护人员---养老床位耦合为约束,采用序列二次规划法求解。结果表明:2025、2030和2035年医疗床位需求分别为8.14万、8.69万和9.17万张,医师需求为5.43万、6.47万和7.30万人,护士需求为5.79万、10.17万和14.04万人;养老床位需求由2.78万张增加至11.12万张,照护人员由1.39万人增加至6.78万人。相较历史趋势外推与等比例扩张,优化策略以1828.54亿元情景等价全生命周期投入实现100%的目标达成度,且结构失衡指标接近0。
  针对问题三,为了使资源扩张与基金可持续性相匹配,本文分别建立基本医疗保险和长期护理保险的年龄成本精算模型。考虑到医疗费用增长、工资增长、就业率、投资收益率、失能发生率和人口迁移均具有随机性,进一步引入共同随机数Monte Carlo模拟,以"2035年基金不出现负储备且不少于12个月支出储备的概率达到95%"为偿付能力约束,在费率网格中搜索最低可行筹资水平。模型给出的医疗保险等价工资费率为8.80%,并配置人均550元财政补助;长期护理保险等价工资费率为1.05%,叠加0.15%的财政等价支持。基准情景下,2035年两项基金储备分别相当于22.46个月和22.31个月支出;3000条随机路径中,满足12个月储备约束的概率分别为96.0%和96.37%,说明方案在给定参数域内具有较好的偿付稳健性。
  综上,本文形成"人口预测---目标量化---资源优化---保险精算---鲁棒性检验"的闭环决策框架。模型通过人口回代误差、极限情景、约束可行性、策略对照、Monte Carlo偿付概率和局部敏感性等多层证据完成验证,并明确区分官方观测、约束回推和情景参数。研究表明,深圳未来的主要矛盾将由一般医疗床位不足逐步转向护理人力、长期照护床位和基金支出增速之间的结构性矛盾;因此,应以基层医疗和社区居家养老为底座,以护理队伍扩容为优先变量,以医疗保险与长期护理保险协同筹资为财务保障,分阶段建设可持续的"民生幸福标杆"。

模型假设:

  1 人口状态封闭性假设 除净迁移外,人口变化由出生、年龄转移和存活率决定;净迁移人口主要进入15---59岁组。
  2 参数分段稳定假设 在2020---2035年内,生育率、存活率和迁移衰减率可在低、中、高情景内视为分段稳定。
  3 服务目标可加总假设 同类床位与人员经过质量校正后可加总为城市级有效供给;不进一步区分行政区。
  4 资源建设不可逆假设 医院床位、养老床位和受训人员存量在相邻目标期不减少,即满足单调性。
  5 需求下界假设 问题一确定的人均目标是问题二必须满足的最低供给水平,允许少量超前配置但对闲置进行惩罚。
  6 结构协同假设 护士与医师、照护人员与养老床位之间存在最低配比,单独增加某一资源不能完全替代其他资源。
  7 基金年度结算假设 保险基金以年度为结算周期,当年结余按给定收益率滚存;不考虑跨地区基金调剂。
  8 风险独立近似假设 工资、医疗费用、投资收益、人口迁移等冲击通过相关系数矩阵生成,未观测的小额风险视为条件独立。
  9 政策等价费率假设 模型中的费率是覆盖给定待遇包所需的等价筹资率,不直接等同于现行法规中的具体缴费比例。
  10 价格口径假设 资源成本使用可审计的情景等价全生命周期现值,目的在于比较策略,不作为项目概算或市场报价。

问题分析:

  问题一分析
  针对深圳 2025、2030、2035 三阶段医疗养老综合保障量化目标构建问题,本题属于人口动力学预测与多指标分层综合评价融合建模问题。核心矛盾是国际医疗养老标准无法直接适配深圳年轻、高人口流动、老龄化加速的城市特征,不能简单照搬单一国别指标;建模先搭建四分组离散人口转移模型,基于七普基准数据预测未来总人口与老龄人口规模,区分本地规划下限、国际标杆上限设计十维保障指标体系,采用熵权客观赋权法消除人工主观偏差,构建分阶段综合保障指数,定量测算各年度发展达标程度,识别护理、长期照护等中长期核心短板,为后续资源配置提供刚性需求基准。
  问题二分析
  基于问题一人口结构与阶段保障目标成果,本题属于带存量不可逆、结构配比约束的多期连续资源优化问题。各类医疗、养老床位与医护、照护人才建设存在长周期、不可缩减的现实约束,单纯分年度独立计算需求会出现阶段性建设峰值、财政投入剧烈波动;以全生命周期折现总成本、建设波动幅度、资源闲置规模为多目标,构建序列二次规划优化模型,绑定医护配比、照护人员与养老床位耦合硬性约束,求解 2025/2030/2035 三期最优资源存量与分阶段新增规模,换算为标准化机构等价数量,同时对比历史外推、等比例扩张两类粗放配置方案,证明多期平滑优化方案兼具低成本与均衡结构优势。
  问题三分析
  依托前两问人口、资源供给数据,本题属于带多随机扰动的医保、长护险年龄分层精算建模问题。医疗费用、工资水平、投资收益、人口迁移、失能比例均存在长期不确定性,仅依靠确定性收支测算无法评估基金破产尾部风险;先构建分年龄组年度收支确定性模型测算基准现金流,引入共同随机数蒙特卡洛模拟生成数千条经济人口冲击路径,设置 "2035 年基金储备不少于 12 个月支出、偿付概率 95%" 硬性风险约束,网格遍历筛选最低合理筹资费率,分别测算职工缴费、财政补助配套标准,绘制费率与偿付概率响应曲线,开展多参数敏感性分析,明确医疗费用增速、老龄化进程是影响基金可持续的核心变量,并配套分层医保、长护险待遇支付实施框架。

模型的建立与求解整体论文缩略图

全部论文请见下方" 只会建模 QQ名片" 点击QQ名片即可

程序代码:

python 复制代码
from __future__ import annotations

import argparse
import json
import math
import warnings
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Dict, List, Tuple

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import font_manager
from scipy.optimize import minimize

warnings.filterwarnings("ignore", category=RuntimeWarning)

SEED = 20200818
MILESTONES = [2025, 2030, 2035]


@dataclass
class ModelParameters:
    """可审计的情景参数。"""

    fertility_rate: float = 0.0090
    net_migration_2020_million: float = 0.030
    migration_decay: float = 0.030
    working_age_transition_width: float = 55.0
    survival_0_14: float = 0.9995
    survival_15_59: float = 0.9988
    survival_60_64: float = 0.9920
    survival_65_plus: float = 0.9720
    medical_cost_inflation: float = 0.048
    wage_growth: float = 0.045
    investment_return: float = 0.030
    employment_rate: float = 0.780
    medical_payroll_rate: float = 0.0880
    ltc_payroll_rate: float = 0.0105
    ltc_fiscal_equivalent_rate: float = 0.0015
    per_capita_medical_subsidy: float = 550.0
    medical_initial_reserve_billion: float = 90.0
    ltc_initial_reserve_billion: float = 10.0


def configure_chinese_font() -> None:
    """设置中文字体,避免图中文字显示为方框。"""
    candidates = [
        "Noto Sans CJK JP",
        "Noto Sans CJK SC",
        "Source Han Sans SC",
        "Microsoft YaHei",
        "SimHei",
        "WenQuanYi Zen Hei",
    ]
    available = {f.name for f in font_manager.fontManager.ttflist}
    for name in candidates:
        if name in available:
            plt.rcParams["font.family"] = name
            break
    plt.rcParams["axes.unicode_minus"] = False
    plt.rcParams["figure.dpi"] = 120
    plt.rcParams["savefig.dpi"] = 220
    plt.rcParams["axes.titleweight"] = "bold"
    plt.rcParams["axes.grid"] = True
    plt.rcParams["grid.alpha"] = 0.22


def official_observations() -> pd.DataFrame:
    """构造公开统计观测表。

    physicians_k、nurses_k、gps_k在2020---2022年公开公报中未统一披露,
    采用2023年每千人口指标与卫生技术人员增长轨迹进行约束回推;
    这些字段在data_status中明确标记为"模型回推"。
    """
    df = pd.DataFrame(
        {
            "year": [2020, 2021, 2022, 2023, 2024, 2025],
            "population_million": [17.560061, 17.6816, 17.6618, 17.7901, 17.9895, 18.2480],
            "medical_institutions": [4686, 5241, 5201, 5431, 5887, 6011],
            "hospitals": [145, 145, 151, 159, 162, 171],
            "medical_beds_k": [62.904, 63.990, 65.720, 69.877, 79.561, 92.327],
            "health_technicians_k": [106.261, 113.284, 118.273, 125.993, 132.806, 139.474],
            "eldercare_beds_k": [12.147, 13.491, 13.922, 13.240, 14.385, np.nan],
            "resident_income_yuan": [64878, 70847, 72718, 76910, 81123, np.nan],
            "gdp_per_capita_yuan": [157600, 173663, 183300, 195230, 205714, np.nan],
        }
    )

    # 2023年公开指标:每千人口执业(助理)医师2.86人、注册护士3.07人。
    physicians_2023 = 2.86 * df.loc[df.year == 2023, "population_million"].iloc[0]
    nurses_2023 = 3.07 * df.loc[df.year == 2023, "population_million"].iloc[0]
    tech_ratio = df["health_technicians_k"] / df.loc[df.year == 2023, "health_technicians_k"].iloc[0]
    df["physicians_k"] = physicians_2023 * tech_ratio
    df["nurses_k"] = nurses_2023 * tech_ratio
    # 全科医生以2020年4.0人/万人为回推起点,逐年平滑接近2025年5.0人/万人规划值。
    gp_rates = np.array([4.0, 4.2, 4.4, 4.6, 4.8, 5.0])
    df["gps_k"] = gp_rates * df["population_million"] / 10.0
    df["data_status"] = [
        "官方观测+人员回推",
        "官方观测+人员回推",
        "官方观测+人员回推",
        "官方观测",
        "官方观测+人员回推",
        "官方观测(养老床位缺失)+人员回推",
    ]
    return df


def preprocess_observations(raw: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """数据预处理:口径统一、缺失诊断、派生指标、稳健异常标记。"""
    df = raw.copy().sort_values("year").reset_index(drop=True)
    # 养老床位2025缺失:仅用于描述图,不用于核心2020基准,使用保守线性外推并保留插补标记。
    df["eldercare_beds_imputed"] = df["eldercare_beds_k"].isna()
    df["eldercare_beds_k"] = df["eldercare_beds_k"].interpolate(method="linear").ffill()
    if df.loc[df.year == 2025, "eldercare_beds_imputed"].iloc[0]:
        growth = (df.loc[df.year == 2024, "eldercare_beds_k"].iloc[0] /
                  df.loc[df.year == 2023, "eldercare_beds_k"].iloc[0] - 1)
        df.loc[df.year == 2025, "eldercare_beds_k"] = (
            df.loc[df.year == 2024, "eldercare_beds_k"].iloc[0] * (1 + growth)
        )

    df["beds_per_1000"] = df["medical_beds_k"] / df["population_million"]
    df["physicians_per_1000"] = df["physicians_k"] / df["population_million"]
    df["nurses_per_1000"] = df["nurses_k"] / df["population_million"]
    df["gps_per_10000"] = df["gps_k"] / df["population_million"] * 10
    df["health_tech_per_1000"] = df["health_technicians_k"] / df["population_million"]

    # 基于中位数绝对偏差(MAD)进行稳健异常诊断,不直接删除政策扩容形成的真实跃迁。
    numeric_cols = [
        "population_million", "medical_beds_k", "health_technicians_k",
        "eldercare_beds_k", "beds_per_1000", "health_tech_per_1000"
    ]
    quality_rows = []
    for col in numeric_cols:
        x = df[col].astype(float)
        med = x.median()
        mad = np.median(np.abs(x - med))
        robust_z = np.zeros(len(x)) if mad == 0 else 0.6745 * (x - med) / mad
        for year, value, z in zip(df.year, x, robust_z):
            quality_rows.append(
                {
                    "year": int(year), "variable": col, "value": float(value),
                    "robust_z": float(z), "is_potential_outlier": bool(abs(z) > 3.5),
                }
            )
    quality = pd.DataFrame(quality_rows)
    return df, quality


def project_population(params: ModelParameters, years: int = 15,
                       migration_multiplier: float = 1.0,
                       fertility_multiplier: float = 1.0,
                       survival65_adjustment: float = 0.0) -> pd.DataFrame:
    """四年龄组离散人口动力学模型。

    状态向量x_t=[0-14岁,15-59岁,60-64岁,65岁及以上],单位为百万人。
    迁移人口主要进入劳动年龄组,净迁移随城市容量约束指数衰减。
    """
    x = np.array([2.653381, 13.965964, 0.375499, 0.565217], dtype=float)
    rows = []
    for step in range(years + 1):
        year = 2020 + step
        rows.append(
            {
                "year": year,
                "age_0_14_million": x[0],
                "age_15_59_million": x[1],
                "age_60_64_million": x[2],
                "age_65_plus_million": x[3],
                "population_million": x.sum(),
                "share_65_plus": x[3] / x.sum(),
                "share_60_plus": (x[2] + x[3]) / x.sum(),
            }
        )
        if step == years:
            break
        young, working, age60, age65 = x
        births = params.fertility_rate * fertility_multiplier * working
        age_out_young = young / 15.0
        age_out_working = working / params.working_age_transition_width
        age_out_60 = age60 / 5.0
        migration = (params.net_migration_2020_million * migration_multiplier *
                     math.exp(-params.migration_decay * step))
        x = np.array(
            [
                (young - age_out_young) * params.survival_0_14 + births,
                (working - age_out_working) * params.survival_15_59
                + age_out_young * 0.999 + migration,
                (age60 - age_out_60) * params.survival_60_64
                + age_out_working * 0.995,
                age65 * np.clip(params.survival_65_plus + survival65_adjustment, 0.94, 0.995)
                + age_out_60 * 0.985,
            ]
        )
    return pd.DataFrame(rows)


def population_scenarios(params: ModelParameters) -> Dict[str, pd.DataFrame]:
    """低、中、高三种人口情景。"""
    return {
        "低增长情景": project_population(params, migration_multiplier=0.55,
                                   fertility_multiplier=0.90, survival65_adjustment=-0.002),
        "基准情景": project_population(params),
        "高增长情景": project_population(params, migration_multiplier=1.60,
                                   fertility_multiplier=1.08, survival65_adjustment=0.002),
    }


def build_target_matrix(pop: pd.DataFrame) -> pd.DataFrame:
    """构建2025、2030、2035年量化目标矩阵。"""
    target = pd.DataFrame(
        {
            "year": MILESTONES,
            "beds_per_1000": [4.5, 4.7, 4.9],
            "physicians_per_1000": [3.0, 3.5, 3.9],
            "nurses_per_1000": [3.2, 5.5, 7.5],
            "gps_per_10000": [5.0, 7.0, 9.0],
            "ltc_beds_per_1000_65p": [25.0, 35.0, 41.0],
            "care_workers_per_1000_65p": [12.5, 18.0, 25.0],
            "medical_insurance_pct": [98.0, 99.0, 99.5],
            "ltc_insurance_pct": [60.0, 90.0, 98.0],
            "community_care_pct": [90.0, 95.0, 98.0],
            "elder_health_management_pct": [85.0, 92.0, 96.0],
        }
    ).set_index("year")

    pop_m = pop.set_index("year").loc[MILESTONES]
    target["population_million"] = pop_m["population_million"]
    target["age_65_plus_million"] = pop_m["age_65_plus_million"]
    return target


def entropy_weights(matrix: np.ndarray) -> np.ndarray:
    """熵权法:输入为非负决策矩阵,返回指标权重。"""
    x = np.asarray(matrix, dtype=float)
    xmin = x.min(axis=0)
    xmax = x.max(axis=0)
    z = (x - xmin) / np.where(xmax - xmin == 0, 1, xmax - xmin) + 1e-12
    p = z / z.sum(axis=0, keepdims=True)
    k = 1.0 / math.log(x.shape[0])
    e = -k * np.sum(p * np.log(p), axis=0)
    d = 1 - e
    return d / d.sum()


def build_indicator_scores(target: pd.DataFrame, obs: pd.DataFrame) -> Tuple[pd.DataFrame, pd.Series]:
    """构建基准---目标四时点指标得分并计算熵权。"""
    pop2020 = obs.loc[obs.year == 2020, "population_million"].iloc[0]
    age65_2020 = 0.565217
    baseline = {
        "beds_per_1000": obs.loc[obs.year == 2020, "beds_per_1000"].iloc[0],
        "physicians_per_1000": obs.loc[obs.year == 2020, "physicians_per_1000"].iloc[0],
        "nurses_per_1000": obs.loc[obs.year == 2020, "nurses_per_1000"].iloc[0],
        "gps_per_10000": obs.loc[obs.year == 2020, "gps_per_10000"].iloc[0],
        "ltc_beds_per_1000_65p": obs.loc[obs.year == 2020, "eldercare_beds_k"].iloc[0] / age65_2020,
        "care_workers_per_1000_65p": 9.7,
        "medical_insurance_pct": 97.5,
        "ltc_insurance_pct": 10.0,
        "community_care_pct": 70.0,
        "elder_health_management_pct": 75.0,
    }
    cols = [
        "beds_per_1000", "physicians_per_1000", "nurses_per_1000", "gps_per_10000",
        "ltc_beds_per_1000_65p", "care_workers_per_1000_65p", "medical_insurance_pct",
        "ltc_insurance_pct", "community_care_pct", "elder_health_management_pct",
    ]
    score = pd.DataFrame(index=[2020] + MILESTONES, columns=cols, dtype=float)
    score.loc[2020] = pd.Series(baseline)
    score.loc[MILESTONES] = target[cols].values
    w = entropy_weights(score.values)
    weights = pd.Series(w, index=cols, name="entropy_weight")
    benchmark = score.loc[2035]
    normalized = score / benchmark
    normalized = normalized.clip(upper=1.2)
    score["composite_index"] = normalized[cols].mul(weights, axis=1).sum(axis=1) * 100
    return score, weights


def resource_demand(target: pd.DataFrame) -> pd.DataFrame:
    """将人均目标映射为资源存量需求,单位均为千人/千张。"""
    demand = pd.DataFrame(index=target.index)
    demand["hospital_beds_k"] = target["beds_per_1000"] * target["population_million"]
    demand["physicians_k"] = target["physicians_per_1000"] * target["population_million"]
    demand["nurses_k"] = target["nurses_per_1000"] * target["population_million"]
    demand["gps_k"] = target["gps_per_10000"] * target["population_million"] / 10.0
    demand["elder_beds_k"] = target["ltc_beds_per_1000_65p"] * target["age_65_plus_million"]
    demand["care_workers_k"] = target["care_workers_per_1000_65p"] * target["age_65_plus_million"]
    return demand


def optimize_resource_plan(demand: pd.DataFrame, obs: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame, Dict]:
    """求解多期资源配置模型。

    目标函数包含:折现建设/培养成本、跨期增量波动惩罚、超前闲置惩罚。
    约束包含:目标需求下界、存量单调性、护医比、照护人员---养老床位耦合。
    """
    resources = list(demand.columns)
    baseline = np.array(
        [
            obs.loc[obs.year == 2020, "medical_beds_k"].iloc[0],
            obs.loc[obs.year == 2020, "physicians_k"].iloc[0],
            obs.loc[obs.year == 2020, "nurses_k"].iloc[0],
            obs.loc[obs.year == 2020, "gps_k"].iloc[0],
            obs.loc[obs.year == 2020, "eldercare_beds_k"].iloc[0],
            5.47,
        ]
    )
    # 每新增千单位的等价全生命周期现值成本,单位:亿元/千单位。
    unit_cost_100m_per_k = np.array([8.5, 12.0, 6.5, 9.0, 3.5, 2.8])
    dem = demand.values.astype(float)
    discount = np.array([(1.03) ** 5, (1.03) ** 10, (1.03) ** 15])
    scale = np.maximum(dem[-1] - baseline, 1.0)

    def objective(z: np.ndarray) -> float:
        x = z.reshape(3, 6)
        prev = np.vstack([baseline, x[:-1]])
        inc = x - prev
        capex = np.sum(inc * unit_cost_100m_per_k / discount[:, None])
        annualized = inc / 5.0
        smooth = 1.5 * np.sum((np.diff(annualized, axis=0) / (scale / 5.0)) ** 2)
        idle = 0.05 * np.sum((np.maximum(x - dem, 0) / np.maximum(dem, 1)) ** 2)
        return float(capex + smooth + idle)

    constraints = [
        {"type": "ineq", "fun": lambda z: (z.reshape(3, 6) - dem).ravel()},
        {"type": "ineq", "fun": lambda z: np.diff(np.vstack([baseline, z.reshape(3, 6)]), axis=0).ravel()},
        {"type": "ineq", "fun": lambda z: z.reshape(3, 6)[:, 2] - np.array([1.05, 1.50, 1.80]) * z.reshape(3, 6)[:, 1]},
        {"type": "ineq", "fun": lambda z: z.reshape(3, 6)[:, 5] - np.array([0.45, 0.50, 0.55]) * z.reshape(3, 6)[:, 4]},
    ]
    x0 = np.maximum(dem, np.linspace(baseline, dem[-1], 4)[1:])
    result = minimize(
        objective, x0.ravel(), method="SLSQP", bounds=[(0, None)] * 18,
        constraints=constraints, options={"maxiter": 3000, "ftol": 1e-10}
    )
    if not result.success:
        raise RuntimeError(f"资源优化未收敛:{result.message}")
    plan = pd.DataFrame(result.x.reshape(3, 6), index=demand.index, columns=resources)
    previous = pd.DataFrame(
        np.vstack([baseline, plan.values[:-1]]), index=plan.index, columns=resources
    )
    additions = plan - previous
    cost_table = additions.mul(unit_cost_100m_per_k, axis=1)
    cost_table["total_nominal_100m_yuan"] = cost_table.sum(axis=1)

    equivalents = pd.DataFrame(index=plan.index)
    equivalents["general_hospital_equiv"] = np.ceil(plan["hospital_beds_k"] / 0.50)
    equivalents["maternal_child_center_equiv"] = np.ceil(target_population_from_plan(plan) / 1.50)
    equivalents["community_health_centers"] = np.ceil(target_population_from_plan(plan) / 0.03)
    equivalents["eldercare_institution_equiv"] = np.ceil(plan["elder_beds_k"] / 0.20)

    meta = {
        "success": bool(result.success),
        "objective_value": float(result.fun),
        "message": str(result.message),
        "baseline": dict(zip(resources, baseline.tolist())),
        "unit_cost_100m_per_k": dict(zip(resources, unit_cost_100m_per_k.tolist())),
        "equivalent_facilities": equivalents.reset_index().to_dict(orient="records"),
    }
    return plan, cost_table, meta


def target_population_from_plan(plan: pd.DataFrame) -> np.ndarray:
    """由医院床位需求和既定床位率反推人口,仅用于机构等价数。"""
    rates = np.array([4.5, 4.7, 4.9])
    return plan["hospital_beds_k"].values / rates


def strategy_comparison(demand: pd.DataFrame, plan: pd.DataFrame, obs: pd.DataFrame,
                        weights: pd.Series) -> pd.DataFrame:
    """比较历史趋势外推、等比例扩张与优化配置三种策略。"""
    resources = list(demand.columns)
    baseline = np.array([
        obs.loc[obs.year == 2020, "medical_beds_k"].iloc[0],
        obs.loc[obs.year == 2020, "physicians_k"].iloc[0],
        obs.loc[obs.year == 2020, "nurses_k"].iloc[0],
        obs.loc[obs.year == 2020, "gps_k"].iloc[0],
        obs.loc[obs.year == 2020, "eldercare_beds_k"].iloc[0], 5.47,
    ])
    unit_cost = np.array([8.5, 12.0, 6.5, 9.0, 3.5, 2.8])
    target2035 = demand.loc[2035].values

    # 历史趋势:按2020---2024各类资源近似CAGR外推,人员使用卫生技术人员增速的70%。
    bed_cagr = (obs.loc[obs.year == 2024, "medical_beds_k"].iloc[0] /
                obs.loc[obs.year == 2020, "medical_beds_k"].iloc[0]) ** (1 / 4) - 1
    elder_cagr = (obs.loc[obs.year == 2024, "eldercare_beds_k"].iloc[0] /
                  obs.loc[obs.year == 2020, "eldercare_beds_k"].iloc[0]) ** (1 / 4) - 1
    tech_cagr = (obs.loc[obs.year == 2024, "health_technicians_k"].iloc[0] /
                 obs.loc[obs.year == 2020, "health_technicians_k"].iloc[0]) ** (1 / 4) - 1
    rates = np.array([bed_cagr, 0.70 * tech_cagr, 0.80 * tech_cagr,
                      0.65 * tech_cagr, elder_cagr, 0.75 * elder_cagr])
    trend = baseline * (1 + rates) ** 15

    uniform_growth = (np.mean(target2035 / baseline)) ** (1 / 15) - 1
    uniform = baseline * (1 + uniform_growth) ** 15
    optimized = plan.loc[2035].values

    rows = []
    resource_weights = np.array([0.22, 0.20, 0.18, 0.10, 0.17, 0.13])
    for name, stock in [("历史趋势外推", trend), ("等比例扩张", uniform), ("优化配置", optimized)]:
        attainment = np.minimum(stock / target2035, 1.0)
        score = float(np.dot(attainment, resource_weights) * 100)
        cost = float(np.maximum(stock - baseline, 0).dot(unit_cost))
        imbalance = float(np.std(stock / target2035))
        rows.append({
            "strategy": name, "target_attainment_score": score,
            "incremental_cost_100m_yuan": cost, "structural_imbalance": imbalance,
            **{f"{r}_2035": v for r, v in zip(resources, stock)},
        })
    return pd.DataFrame(rows)


def deterministic_funds(pop: pd.DataFrame, params: ModelParameters) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """医疗保险与长期护理保险确定性基金路径。"""
    ages = pop.set_index("year").loc[2020:2035, [
        "age_0_14_million", "age_15_59_million", "age_60_64_million", "age_65_plus_million"
    ]].values
    base_med_cost = np.array([4500.0, 6500.0, 14000.0, 22000.0])
    med_reserve = params.medical_initial_reserve_billion
    ltc_reserve = params.ltc_initial_reserve_billion
    med_rows, ltc_rows = [], []
    for idx, year in enumerate(range(2021, 2036), start=1):
        ag = ages[idx]
        wage = 150000.0 * (1 + params.wage_growth) ** idx
        payroll = ag[1] * 1e6 * params.employment_rate * wage / 1e9
        reimbursement = 0.80 + (0.85 - 0.80) * idx / 15.0
        med_revenue = (payroll * params.medical_payroll_rate +
                       ag.sum() * 1e6 * params.per_capita_medical_subsidy / 1e9)
        med_expense = (ag * 1e6 * base_med_cost * (1 + params.medical_cost_inflation) ** idx).sum() / 1e9
        med_expense *= reimbursement * 1.03
        med_reserve = (med_reserve + med_revenue - med_expense) * (1 + params.investment_return)
        med_rows.append({
            "year": year, "revenue_billion": med_revenue, "expense_billion": med_expense,
            "reserve_billion": med_reserve, "reserve_months": 12 * med_reserve / med_expense,
            "payroll_rate": params.medical_payroll_rate,
        })

        dependent = ag[2] * 1e6 * 0.04 + ag[3] * 1e6 * 0.18
        ltc_revenue = payroll * (params.ltc_payroll_rate + params.ltc_fiscal_equivalent_rate)
        ltc_unit_cost = 60000.0 * (1 + 0.045) ** idx
        ltc_expense = dependent * ltc_unit_cost / 1e9 * 0.75 * 1.05
        ltc_reserve = (ltc_reserve + ltc_revenue - ltc_expense) * (1 + params.investment_return)
        ltc_rows.append({
            "year": year, "revenue_billion": ltc_revenue, "expense_billion": ltc_expense,
            "reserve_billion": ltc_reserve, "reserve_months": 12 * ltc_reserve / ltc_expense,
            "payroll_rate": params.ltc_payroll_rate,
        })
    return pd.DataFrame(med_rows), pd.DataFrame(ltc_rows)


def generate_shocks(n_paths: int, seed: int = SEED) -> Dict[str, np.ndarray]:
    rng = np.random.default_rng(seed)
    t = 15
    return {
        "pop": rng.normal(0.0, 0.003, (n_paths, t)),
        "medical_inflation": rng.normal(0.048, 0.012, (n_paths, t)),
        "ltc_inflation": rng.normal(0.045, 0.012, (n_paths, t)),
        "wage_growth": rng.normal(0.045, 0.010, (n_paths, t)),
        "return": rng.normal(0.030, 0.015, (n_paths, t)),
        "employment": np.clip(rng.normal(0.780, 0.020, (n_paths, t)), 0.70, 0.85),
        "disability": np.clip(rng.normal(1.0, 0.08, (n_paths, t)), 0.75, 1.30),
    }


def monte_carlo_funds(pop: pd.DataFrame, med_rate: float, ltc_rate: float,
                      shocks: Dict[str, np.ndarray], params: ModelParameters) -> Dict:
    """联合Monte Carlo精算模拟。"""
    n_paths, horizon = shocks["pop"].shape
    ages = pop.set_index("year").loc[2020:2035, [
        "age_0_14_million", "age_15_59_million", "age_60_64_million", "age_65_plus_million"
    ]].values
    med_reserve = np.full(n_paths, params.medical_initial_reserve_billion)
    ltc_reserve = np.full(n_paths, params.ltc_initial_reserve_billion)
    med_min = med_reserve.copy()
    ltc_min = ltc_reserve.copy()
    wage = np.full(n_paths, 150000.0)
    med_cost_factor = np.ones(n_paths)
    ltc_cost_factor = np.ones(n_paths)
    pop_factor = np.ones(n_paths)
    base_med_cost = np.array([4500.0, 6500.0, 14000.0, 22000.0])
    med_mean_path, ltc_mean_path = [], []
    med_q05, med_q95, ltc_q05, ltc_q95 = [], [], [], []
    med_expense = np.ones(n_paths)
    ltc_expense = np.ones(n_paths)

    for t in range(horizon):
        pop_factor *= 1 + shocks["pop"][:, t]
        wage *= 1 + shocks["wage_growth"][:, t]
        med_cost_factor *= 1 + shocks["medical_inflation"][:, t]
        ltc_cost_factor *= 1 + shocks["ltc_inflation"][:, t]
        ag = ages[t + 1][None, :] * pop_factor[:, None]
        payroll = ag[:, 1] * 1e6 * shocks["employment"][:, t] * wage / 1e9
        reimbursement = 0.80 + (0.85 - 0.80) * (t + 1) / 15.0
        med_revenue = payroll * med_rate + ag.sum(axis=1) * 1e6 * params.per_capita_medical_subsidy / 1e9
        med_expense = (ag * 1e6 * base_med_cost[None, :] * med_cost_factor[:, None]).sum(axis=1) / 1e9
        med_expense *= reimbursement * 1.03
        med_reserve = (med_reserve + med_revenue - med_expense) * (1 + shocks["return"][:, t])
        med_min = np.minimum(med_min, med_reserve)

        dependent = (ag[:, 2] * 1e6 * 0.04 + ag[:, 3] * 1e6 * 0.18) * shocks["disability"][:, t]
        ltc_revenue = payroll * (ltc_rate + params.ltc_fiscal_equivalent_rate)
        ltc_expense = dependent * 60000.0 * ltc_cost_factor / 1e9 * 0.75 * 1.05
        ltc_reserve = (ltc_reserve + ltc_revenue - ltc_expense) * (1 + shocks["return"][:, t])
        ltc_min = np.minimum(ltc_min, ltc_reserve)

        med_mean_path.append(float(np.mean(med_reserve)))
        ltc_mean_path.append(float(np.mean(ltc_reserve)))
        med_q05.append(float(np.quantile(med_reserve, 0.05)))
        med_q95.append(float(np.quantile(med_reserve, 0.95)))
        ltc_q05.append(float(np.quantile(ltc_reserve, 0.05)))
        ltc_q95.append(float(np.quantile(ltc_reserve, 0.95)))

    med_months = 12 * med_reserve / med_expense
    ltc_months = 12 * ltc_reserve / ltc_expense
    return {
        "medical_nonnegative_probability": float(np.mean(med_min >= 0)),
        "medical_12month_probability": float(np.mean(med_months >= 12)),
        "medical_final_median_billion": float(np.median(med_reserve)),
        "medical_final_months_median": float(np.median(med_months)),
        "ltc_nonnegative_probability": float(np.mean(ltc_min >= 0)),
        "ltc_12month_probability": float(np.mean(ltc_months >= 12)),
        "ltc_final_median_billion": float(np.median(ltc_reserve)),
        "ltc_final_months_median": float(np.median(ltc_months)),
        "medical_final_reserve": med_reserve,
        "ltc_final_reserve": ltc_reserve,
        "medical_final_months": med_months,
        "ltc_final_months": ltc_months,
        "medical_mean_path": np.array(med_mean_path),
        "ltc_mean_path": np.array(ltc_mean_path),
        "medical_q05": np.array(med_q05),
        "medical_q95": np.array(med_q95),
        "ltc_q05": np.array(ltc_q05),
        "ltc_q95": np.array(ltc_q95),
    }
全部论文请见下方" 只会建模 QQ名片" 点击QQ名片即可
相关推荐
观远数据4 小时前
AI+BI时代的数据合规三重门:传输、存储、消费如何一体化管控
大数据·数据分析
旖旎夜光4 小时前
LeetCode 904:水果成篮(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
@insist1234 小时前
系统集成项目管理工程师-数据分析应用与数据安全
数据分析·软考·系统集成项目管理工程师·软考中项·软件水平考试
怪奇云呼军5 小时前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
乐观勇敢坚强的老彭6 小时前
C++ 竞赛常用算法模板速查表
开发语言·c++·算法
高洁016 小时前
工信部教考中心证书
人工智能·深度学习·算法·机器学习·知识图谱
我变成萤火虫6 小时前
河南萌新联赛2026第(三)场:郑州轻工业大学
数据结构·c++·算法·贪心算法·stl·深度优先·哈希算法
Dr.kangder6 小时前
嵌入式面试总结(十九)——内存泄露
单片机·算法·面试·职场和发展·架构·硬件架构
Herbert_hwt7 小时前
【C语言基础】常量、选择结构与运算符全解析
c语言·开发语言·算法
Suxing97 小时前
C语言基础分享:从“租房”到“拆迁”,C语言动态内存管理
java·数据结构·算法