From d4d7f7d94bbd07293b50c81d4a98266423991faf Mon Sep 17 00:00:00 2001 From: ntnt Date: Mon, 19 Jan 2026 12:59:03 +0800 Subject: [PATCH] P3: sens and readme --- task3/07_sensitivity.py | 400 +++++++++++++ task3/07_sensitivity.xlsx | Bin 0 -> 12164 bytes task3/README.md | 1113 +++++++++++++++++++------------------ 3 files changed, 966 insertions(+), 547 deletions(-) create mode 100644 task3/07_sensitivity.py create mode 100644 task3/07_sensitivity.xlsx diff --git a/task3/07_sensitivity.py b/task3/07_sensitivity.py new file mode 100644 index 0000000..9e25e28 --- /dev/null +++ b/task3/07_sensitivity.py @@ -0,0 +1,400 @@ +""" +Task 3 - Step 7: 敏感性分析 +============================ + +分析以下参数对模型输出的影响: +1. 合并比例 r_merge: [1/3, 1/2, 2/3] +2. 距离阈值 l_max: [30, 40, 50, 60, 70] +3. 容量上限 μ_sum_max: [400, 425, 450, 475, 500] +4. CV阈值: [0.3, 0.4, 0.5, 0.6] + +输出: 07_sensitivity.xlsx (各参数对E1', E2', F1', R1的影响) +""" + +import pandas as pd +import numpy as np +from scipy import stats +import warnings +warnings.filterwarnings('ignore') + +# ============================================ +# 基础参数和函数 +# ============================================ +Q = 400 +QUALITY_THRESHOLD = 250 +SHORTFALL_THRESHOLD = 0.8 + +def quality_factor(mu_total): + return min(1.0, QUALITY_THRESHOLD / mu_total) if mu_total > 0 else 1.0 + +def expected_service(q, mu, sigma): + if sigma == 0: + return min(mu, q) + z = (q - mu) / sigma + return mu * stats.norm.cdf(z) - sigma * stats.norm.pdf(z) + q * (1 - stats.norm.cdf(z)) + +def gini_coefficient(values): + values = np.array(values) + values = values[~np.isnan(values)] + if len(values) == 0: + return 0 + values = np.sort(values) + n = len(values) + cumsum = np.cumsum(values) + return (2 * np.sum((np.arange(1, n + 1) * values)) - (n + 1) * cumsum[-1]) / (n * cumsum[-1]) if cumsum[-1] > 0 else 0 + +def shortfall_probability(q, mu, sigma, threshold=0.8): + if sigma == 0: + return 0 if q >= mu * threshold else 1 + critical_demand = q / threshold + return 1 - stats.norm.cdf((critical_demand - mu) / sigma) + +def optimal_allocation(mu_i, sigma_i, mu_j, sigma_j, Q=400): + if sigma_i + sigma_j == 0: + return mu_i + return (sigma_j * mu_i + sigma_i * (Q - mu_j)) / (sigma_i + sigma_j) + +def calc_distance(lat1, lon1, lat2, lon2): + lat_avg = (lat1 + lat2) / 2 + lat_avg_rad = np.radians(lat_avg) + delta_lat = lat1 - lat2 + delta_lon = lon1 - lon2 + return 69.0 * np.sqrt(delta_lat**2 + (np.cos(lat_avg_rad) * delta_lon)**2) + +# ============================================ +# 完整流水线函数 +# ============================================ +def run_pipeline(sites_df, l_max=50, mu_sum_max=450, cv_max=0.5, merge_ratio=0.5): + """ + 运行完整的Task 3流水线,返回评估指标 + """ + sites = sites_df.copy() + sites['cv'] = sites['sigma'] / sites['mu'] + n = len(sites) + + # Step 1: 计算距离矩阵 + distance_matrix = np.zeros((n, n)) + for i in range(n): + for j in range(n): + if i != j: + distance_matrix[i, j] = calc_distance( + sites.iloc[i]['lat'], sites.iloc[i]['lon'], + sites.iloc[j]['lat'], sites.iloc[j]['lon'] + ) + + # Step 2: 配对筛选 + candidates = [] + for i in range(n): + for j in range(i + 1, n): + site_i = sites.iloc[i] + site_j = sites.iloc[j] + dist = distance_matrix[i, j] + + if dist > l_max: + continue + mu_sum = site_i['mu'] + site_j['mu'] + if mu_sum > mu_sum_max: + continue + if site_i['cv'] > cv_max or site_j['cv'] > cv_max: + continue + + sigma_sq_sum = site_i['sigma']**2 + site_j['sigma']**2 + value = (1.0 * mu_sum / Q - 0.3 * dist / l_max - 0.5 * sigma_sq_sum / mu_sum**2) + + candidates.append({ + 'idx_i': i, 'idx_j': j, + 'site_i_id': site_i['site_id'], 'site_j_id': site_j['site_id'], + 'distance': dist, + 'mu_i': site_i['mu'], 'mu_j': site_j['mu'], + 'sigma_i': site_i['sigma'], 'sigma_j': site_j['sigma'], + 'k_i': site_i['k'], 'k_j': site_j['k'], + 'mu_tilde_i': site_i['mu_tilde'], 'mu_tilde_j': site_j['mu_tilde'], + 'value': value + }) + + if len(candidates) == 0: + # 无可行配对,返回Task 1的指标 + E1 = (sites['k'] * sites['mu']).sum() + E2 = sum(sites['k'] * sites['mu'].apply(quality_factor) * sites['mu']) + rates = [row['k'] * row['mu'] / row['mu_tilde'] for _, row in sites.iterrows()] + F1 = gini_coefficient(rates) + F2 = min(rates) + return { + 'num_pairs': 0, 'num_dual_visits': 0, + 'E1': E1, 'E2': E2, 'F1': F1, 'F2': F2, 'R1': 0, 'RS': 0 + } + + # 贪心配对选择 + df_cand = pd.DataFrame(candidates).sort_values('value', ascending=False) + selected = [] + used = set() + for _, row in df_cand.iterrows(): + if row['idx_i'] not in used and row['idx_j'] not in used: + selected.append(row.to_dict()) + used.add(row['idx_i']) + used.add(row['idx_j']) + + # Step 3: 计算最优分配 + for pair in selected: + q_star = optimal_allocation(pair['mu_i'], pair['sigma_i'], + pair['mu_j'], pair['sigma_j'], Q) + pair['q_final'] = q_star + pair['E_Si'] = expected_service(q_star, pair['mu_i'], pair['sigma_i']) + pair['E_Sj'] = expected_service(Q - q_star, pair['mu_j'], pair['sigma_j']) + pair['E_total'] = pair['E_Si'] + pair['E_Sj'] + + # Step 4: 重分配访问次数 + sites['k_single'] = sites['k'].copy() + sites['k_dual'] = 0 + + pair_k = {} + for pair in selected: + k_i, k_j = pair['k_i'], pair['k_j'] + k_ij = int(min(k_i, k_j) * merge_ratio) + if k_ij >= min(k_i, k_j): + k_ij = min(k_i, k_j) - 1 + if k_ij < 1: + k_ij = 0 + + idx_i, idx_j = pair['idx_i'], pair['idx_j'] + sites.loc[idx_i, 'k_single'] = k_i - k_ij + sites.loc[idx_i, 'k_dual'] = k_ij + sites.loc[idx_j, 'k_single'] = k_j - k_ij + sites.loc[idx_j, 'k_dual'] = k_ij + pair_k[(pair['site_i_id'], pair['site_j_id'])] = k_ij + + # 计算释放槽位并重分配 + total_single = sites['k_single'].sum() + total_dual = sum(pair_k.values()) + delta_N = 730 - (total_single + total_dual) + + if delta_N > 0: + total_demand = sites['mu_tilde'].sum() + sites['k_extra'] = (delta_N * sites['mu_tilde'] / total_demand).apply(np.floor).astype(int) + remainder = delta_N - sites['k_extra'].sum() + if remainder > 0: + fractional = delta_N * sites['mu_tilde'] / total_demand - sites['k_extra'] + top_idx = fractional.nlargest(int(remainder)).index + sites.loc[top_idx, 'k_extra'] += 1 + sites['k_single_final'] = sites['k_single'] + sites['k_extra'] + else: + sites['k_single_final'] = sites['k_single'] + + # Step 5: 计算指标 + # E1' + E1 = (sites['k_single_final'] * sites['mu']).sum() + for pair in selected: + k_ij = pair_k.get((pair['site_i_id'], pair['site_j_id']), 0) + E1 += k_ij * pair['E_total'] + + # E2' + E2 = sum(sites['k_single_final'] * sites['mu'].apply(quality_factor) * sites['mu']) + for pair in selected: + k_ij = pair_k.get((pair['site_i_id'], pair['site_j_id']), 0) + mu_sum = pair['mu_i'] + pair['mu_j'] + q_factor = quality_factor(mu_sum) + E2 += k_ij * q_factor * pair['E_total'] + + # 满足率 + site_satisfaction = {} + for idx, row in sites.iterrows(): + r = row['k_single_final'] * row['mu'] / row['mu_tilde'] if row['mu_tilde'] > 0 else 0 + site_satisfaction[row['site_id']] = r + + for pair in selected: + k_ij = pair_k.get((pair['site_i_id'], pair['site_j_id']), 0) + r_i = k_ij * pair['E_Si'] / pair['mu_tilde_i'] if pair['mu_tilde_i'] > 0 else 0 + r_j = k_ij * pair['E_Sj'] / pair['mu_tilde_j'] if pair['mu_tilde_j'] > 0 else 0 + site_satisfaction[pair['site_i_id']] += r_i + site_satisfaction[pair['site_j_id']] += r_j + + rates = list(site_satisfaction.values()) + F1 = gini_coefficient(rates) + F2 = min(rates) if rates else 0 + + # R1: 缺口风险 + shortfall_probs = [] + for pair in selected: + q = pair['q_final'] + p_i = shortfall_probability(q, pair['mu_i'], pair['sigma_i']) + p_j = shortfall_probability(Q - q, pair['mu_j'], pair['sigma_j']) + shortfall_probs.append(1 - (1 - p_i) * (1 - p_j)) + R1 = np.mean(shortfall_probs) if shortfall_probs else 0 + + # RS: 资源节省率 + RS = total_dual / 730 + + return { + 'num_pairs': len(selected), + 'num_dual_visits': total_dual, + 'E1': E1, 'E2': E2, 'F1': F1, 'F2': F2, 'R1': R1, 'RS': RS + } + +# ============================================ +# 主程序 +# ============================================ +print("=" * 60) +print("Task 3 - Step 7: 敏感性分析") +print("=" * 60) + +# 读取基础数据 +sites_df = pd.read_excel('../task1/03_allocate.xlsx') +print(f"\n读取站点数据: {len(sites_df)} 个站点") + +# 基准参数 +BASE_L_MAX = 50 +BASE_MU_SUM_MAX = 450 +BASE_CV_MAX = 0.5 +BASE_MERGE_RATIO = 0.5 + +# 计算基准结果 +print(f"\n计算基准结果...") +base_result = run_pipeline(sites_df, BASE_L_MAX, BASE_MU_SUM_MAX, BASE_CV_MAX, BASE_MERGE_RATIO) +print(f"基准: E1={base_result['E1']:.0f}, E2={base_result['E2']:.0f}, F1={base_result['F1']:.4f}, R1={base_result['R1']:.4f}") + +# ============================================ +# 敏感性分析 +# ============================================ +all_results = [] + +# 1. 合并比例敏感性 +print(f"\n" + "-" * 40) +print("1. 合并比例敏感性 (merge_ratio)") +print("-" * 40) + +merge_ratios = [1/3, 0.5, 2/3] +for mr in merge_ratios: + result = run_pipeline(sites_df, BASE_L_MAX, BASE_MU_SUM_MAX, BASE_CV_MAX, mr) + result['param'] = 'merge_ratio' + result['param_value'] = mr + all_results.append(result) + print(f" merge_ratio={mr:.3f}: pairs={result['num_pairs']}, dual={result['num_dual_visits']}, " + f"E1={result['E1']:.0f}, E2={result['E2']:.0f}, F1={result['F1']:.4f}, R1={result['R1']:.4f}") + +# 2. 距离阈值敏感性 +print(f"\n" + "-" * 40) +print("2. 距离阈值敏感性 (l_max)") +print("-" * 40) + +l_max_values = [30, 40, 50, 60, 70] +for lm in l_max_values: + result = run_pipeline(sites_df, lm, BASE_MU_SUM_MAX, BASE_CV_MAX, BASE_MERGE_RATIO) + result['param'] = 'l_max' + result['param_value'] = lm + all_results.append(result) + print(f" l_max={lm}: pairs={result['num_pairs']}, dual={result['num_dual_visits']}, " + f"E1={result['E1']:.0f}, E2={result['E2']:.0f}, F1={result['F1']:.4f}, R1={result['R1']:.4f}") + +# 3. 容量上限敏感性 +print(f"\n" + "-" * 40) +print("3. 容量上限敏感性 (mu_sum_max)") +print("-" * 40) + +mu_sum_values = [400, 425, 450, 475, 500] +for ms in mu_sum_values: + result = run_pipeline(sites_df, BASE_L_MAX, ms, BASE_CV_MAX, BASE_MERGE_RATIO) + result['param'] = 'mu_sum_max' + result['param_value'] = ms + all_results.append(result) + print(f" mu_sum_max={ms}: pairs={result['num_pairs']}, dual={result['num_dual_visits']}, " + f"E1={result['E1']:.0f}, E2={result['E2']:.0f}, F1={result['F1']:.4f}, R1={result['R1']:.4f}") + +# 4. CV阈值敏感性 +print(f"\n" + "-" * 40) +print("4. CV阈值敏感性 (cv_max)") +print("-" * 40) + +cv_max_values = [0.3, 0.4, 0.5, 0.6] +for cv in cv_max_values: + result = run_pipeline(sites_df, BASE_L_MAX, BASE_MU_SUM_MAX, cv, BASE_MERGE_RATIO) + result['param'] = 'cv_max' + result['param_value'] = cv + all_results.append(result) + print(f" cv_max={cv}: pairs={result['num_pairs']}, dual={result['num_dual_visits']}, " + f"E1={result['E1']:.0f}, E2={result['E2']:.0f}, F1={result['F1']:.4f}, R1={result['R1']:.4f}") + +# ============================================ +# 汇总分析 +# ============================================ +df_results = pd.DataFrame(all_results) + +print(f"\n" + "=" * 60) +print("敏感性分析汇总") +print("=" * 60) + +# 计算各参数的影响范围 +for param in ['merge_ratio', 'l_max', 'mu_sum_max', 'cv_max']: + subset = df_results[df_results['param'] == param] + print(f"\n{param}:") + print(f" E1 变化范围: [{subset['E1'].min():.0f}, {subset['E1'].max():.0f}], " + f"变化幅度: {(subset['E1'].max() - subset['E1'].min()) / base_result['E1'] * 100:.2f}%") + print(f" E2 变化范围: [{subset['E2'].min():.0f}, {subset['E2'].max():.0f}], " + f"变化幅度: {(subset['E2'].max() - subset['E2'].min()) / base_result['E2'] * 100:.2f}%") + print(f" F1 变化范围: [{subset['F1'].min():.4f}, {subset['F1'].max():.4f}]") + print(f" R1 变化范围: [{subset['R1'].min():.4f}, {subset['R1'].max():.4f}]") + +# ============================================ +# 保存结果 +# ============================================ +OUTPUT_FILE = '07_sensitivity.xlsx' + +with pd.ExcelWriter(OUTPUT_FILE, engine='openpyxl') as writer: + # Sheet 1: 所有结果 + df_results.to_excel(writer, sheet_name='all_results', index=False) + + # Sheet 2: 合并比例敏感性 + df_merge = df_results[df_results['param'] == 'merge_ratio'].copy() + df_merge.to_excel(writer, sheet_name='merge_ratio', index=False) + + # Sheet 3: 距离阈值敏感性 + df_lmax = df_results[df_results['param'] == 'l_max'].copy() + df_lmax.to_excel(writer, sheet_name='l_max', index=False) + + # Sheet 4: 容量上限敏感性 + df_musum = df_results[df_results['param'] == 'mu_sum_max'].copy() + df_musum.to_excel(writer, sheet_name='mu_sum_max', index=False) + + # Sheet 5: CV阈值敏感性 + df_cv = df_results[df_results['param'] == 'cv_max'].copy() + df_cv.to_excel(writer, sheet_name='cv_max', index=False) + + # Sheet 6: 基准结果 + base_df = pd.DataFrame([{ + 'param': 'baseline', + 'l_max': BASE_L_MAX, + 'mu_sum_max': BASE_MU_SUM_MAX, + 'cv_max': BASE_CV_MAX, + 'merge_ratio': BASE_MERGE_RATIO, + **base_result + }]) + base_df.to_excel(writer, sheet_name='baseline', index=False) + + # Sheet 7: 汇总统计 + summary_rows = [] + for param in ['merge_ratio', 'l_max', 'mu_sum_max', 'cv_max']: + subset = df_results[df_results['param'] == param] + summary_rows.append({ + 'param': param, + 'E1_min': subset['E1'].min(), + 'E1_max': subset['E1'].max(), + 'E1_range_pct': (subset['E1'].max() - subset['E1'].min()) / base_result['E1'] * 100, + 'E2_min': subset['E2'].min(), + 'E2_max': subset['E2'].max(), + 'E2_range_pct': (subset['E2'].max() - subset['E2'].min()) / base_result['E2'] * 100, + 'F1_min': subset['F1'].min(), + 'F1_max': subset['F1'].max(), + 'R1_min': subset['R1'].min(), + 'R1_max': subset['R1'].max() + }) + df_summary = pd.DataFrame(summary_rows) + df_summary.to_excel(writer, sheet_name='summary', index=False) + +print(f"\n结果已保存至: {OUTPUT_FILE}") +print(" - Sheet 'all_results': 所有结果") +print(" - Sheet 'merge_ratio': 合并比例敏感性") +print(" - Sheet 'l_max': 距离阈值敏感性") +print(" - Sheet 'mu_sum_max': 容量上限敏感性") +print(" - Sheet 'cv_max': CV阈值敏感性") +print(" - Sheet 'baseline': 基准结果") +print(" - Sheet 'summary': 汇总统计") +print("\n" + "=" * 60) diff --git a/task3/07_sensitivity.xlsx b/task3/07_sensitivity.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..efcfccffa326f2f2e0bfb8ee3a950a5b5661d58e GIT binary patch literal 12164 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 literal 0 HcmV?d00001 diff --git a/task3/README.md b/task3/README.md index 89a0351..0d3560b 100644 --- a/task3/README.md +++ b/task3/README.md @@ -9,128 +9,324 @@ 3. **解析最优分配策略**:推导出闭式解 $q^* = \frac{\sigma_j \mu_i + \sigma_i (Q - \mu_j)}{\sigma_i + \sigma_j}$ 4. **风险可控的全局调度**:在提升效率的同时,将服务缺口风险控制在可接受范围 +**实际运行结果**: +- 总服务量提升 **16.9%**(140,121 → 163,777) +- 质量加权服务量提升 **5.3%**(131,673 → 138,699) +- 资源节省 **19.5%**(142次双站点访问) +- 服务缺口风险 **17.1%**(可控范围) + --- -## 1. 问题本质重析 +## 完整流程图 + +### Mermaid版本(GitHub可渲染) + +```mermaid +flowchart TB + subgraph INPUT["数据输入"] + A[Task 1 结果
70站点 + 730次分配
task1/03_allocate.xlsx] + end + + subgraph TASK3["TASK 3: 双站点同车优化"] + direction TB + + subgraph CORE["核心流程 ✅ 已完成"] + subgraph PAIRING["阶段1: 配对选择"] + B1[01_distance.py
距离矩阵计算] + B2[02_pairing.py
可行性筛选+贪心选择] + B1 --> B2 + end + + subgraph ALLOCATION["阶段2: 分配优化"] + C1[03_allocation.py
最优分配q*计算] + end + + subgraph SCHEDULING["阶段3: 全局调度"] + D1[04_reschedule.py
访问次数重分配] + D2[05_calendar.py
日历排程生成] + D1 --> D2 + end + + subgraph EVALUATION["阶段4: 效果评估"] + E1[06_evaluate.py
E1',E2',F1',F2',R1,RS] + end + + B2 --> C1 --> D1 + D2 --> E1 + end + + subgraph VALIDATE["结果验证 ✅ 已完成"] + V1[约束满足检验
每日2事件、总730次] + V2[与Task 1对比
指标改进验证] + V3[分配合理性
q*边界检查] + end + + subgraph SENSITIVITY["敏感性分析 ✅ 已完成"] + S1[07_sensitivity.py
4参数扫描] + S2[合并比例: 1/3,1/2,2/3] + S3[距离阈值: 30-70mi] + S4[容量上限: 400-500] + S5[CV阈值: 0.3-0.6] + S1 --> S2 + S1 --> S3 + S1 --> S4 + S1 --> S5 + end + + subgraph VISUAL["可视化 ⏳ 待实现"] + P1[Fig.1 站点配对地图] + P2[Fig.2 分配策略散点图] + P3[Fig.3 敏感性曲线] + P4[Fig.4 日历热力图] + P5[Fig.5 风险分布图] + end + + CORE --> VALIDATE + CORE --> SENSITIVITY + VALIDATE --> VISUAL + SENSITIVITY --> VISUAL + end + + subgraph OUTPUT["输出文件"] + F1[01_distance.xlsx
距离矩阵] + F2[02_pairing.xlsx
34对配对] + F3[03_allocation.xlsx
最优分配] + F4[04_reschedule.xlsx
访问次数] + F5[05_calendar.xlsx
365天排程] + F6[06_evaluate.xlsx
效果对比] + F7[07_sensitivity.xlsx
敏感性结果] + end + + A --> B1 + E1 --> F1 + E1 --> F2 + E1 --> F3 + E1 --> F4 + E1 --> F5 + E1 --> F6 + S1 --> F7 + + style CORE fill:#90EE90 + style VALIDATE fill:#90EE90 + style SENSITIVITY fill:#90EE90 + style VISUAL fill:#FFE4B5 +``` + +### ASCII版本(详细) + +``` +┌─────────────────────────────────────────────────────────────────────────────────────────────┐ +│ TASK 3 完整流程 │ +├─────────────────────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ 核心流程 [已完成 ✓] │ │ +│ │ │ │ +│ │ task1/03_allocate.xlsx │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │ +│ │ │ 01_distance.py │ ──▶ │ 02_pairing.py │ ──▶ │ 03_allocation.py │ │ │ +│ │ │ 距离矩阵70×70 │ │ 34对配对选择 │ │ 最优分配q* │ │ │ +│ │ └──────────────────┘ └──────────────────┘ └────────┬─────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │ +│ │ │ 06_evaluate.py │ ◀── │ 05_calendar.py │ ◀── │ 04_reschedule.py │ │ │ +│ │ │ 效果评估对比 │ │ 365天日历排程 │ │ 访问次数重分配 │ │ │ +│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌─────────────┼─────────────┬───────────────────────────┐ │ +│ │ │ │ │ │ +│ ▼ ▼ ▼ ▼ │ +│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ 结果验证 [✓] │ │ 敏感性分析 [✓] │ │ 可视化 [待实现] │ │ 输出文件 │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ ✓ 每日2事件 │ │ 07_sensitivity │ │ □ 配对地图 │ │ 01_distance.xlsx │ │ +│ │ ✓ 总730次访问 │ │ ┌──────────────┐ │ │ □ 分配散点图 │ │ 02_pairing.xlsx │ │ +│ │ ✓ q*边界检查 │ │ │合并比例 │ │ │ □ 敏感性曲线 │ │ 03_allocation.xlsx│ │ +│ │ ✓ Task 1对比 │ │ │ 1/3,1/2,2/3 │ │ │ □ 日历热力图 │ │ 04_reschedule.xlsx│ │ +│ │ │ │ ├──────────────┤ │ │ □ 风险分布图 │ │ 05_calendar.xlsx │ │ +│ │ 结论: │ │ │距离阈值 │ │ │ │ │ 06_evaluate.xlsx │ │ +│ │ E1↑16.9% │ │ │ 30-70 mi │ │ │ 图表清单: │ │ 07_sensitivity.xlsx│ │ +│ │ E2↑5.3% │ │ ├──────────────┤ │ │ Fig.1-5 │ │ │ │ +│ │ RS=19.5% │ │ │容量上限 │ │ │ │ │ │ │ +│ │ R1=17.1% │ │ │ 400-500 │ │ │ │ │ │ │ +│ │ │ │ ├──────────────┤ │ │ │ │ │ │ +│ │ │ │ │CV阈值 │ │ │ │ │ │ │ +│ │ │ │ │ 0.3-0.6 │ │ │ │ │ │ │ +│ │ │ │ └──────────────┘ │ │ │ │ │ │ +│ └──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 1. 问题本质与底层逻辑 ### 1.1 题目核心要求 题目原文关键句: > "decide on the amount of food to dispense at the first site **(since without pre-registration the demand at the next site is not known for sure)**" +**为什么这是核心难点?** + +传统的单站点访问:需求已知(或可估计),分配是确定性问题。 + +双站点访问的困境: +``` +卡车出发时 ──▶ 到达站点i ──▶ 服务完成 ──▶ 前往站点j ──▶ 服务站点j + │ │ │ │ + │ 需求D_i实现 │ 需求D_j实现 + │ │ │ │ + └── 决策点:预留多少给i? └── 剩余Q-S_i给j +``` + **问题本质**:这是一个**两阶段随机优化问题(Two-Stage Stochastic Optimization)** +- **第一阶段决策**:在出发前决定 $q$(给站点i预留多少) +- **第二阶段决策**:观测到 $D_i$ 后,$S_i = \min(D_i, q)$,剩余 $Q - S_i$ 给站点j -- **第一阶段决策**:在出发前/到达第一站点时,决定分配量 $q$ -- **第二阶段决策**:观测到第一站点实际需求 $D_1$ 后,将剩余食物分配给第二站点 +### 1.2 为什么采用双站点模式? -### 1.2 双站点模式的战略价值 +**底层逻辑**:资源效率优化 -| 收益 | 成本/风险 | -|------|----------| -| 释放卡车次数用于其他站点 | 单次服务两站点可能均不充分 | -| 低需求站点合并提升效率 | 需求波动导致分配失衡 | -| 减少志愿者调度复杂度 | 行程时间增加 | +| 单站点模式 | 双站点模式 | +|-----------|-----------| +| 每天2次独立访问 | 每天可能1次双站点+1次单站点 | +| 730次/年 | 释放部分槽位 | +| 灵活但资源密集 | 效率更高但有风险 | -**核心权衡**:资源节省 vs 服务质量风险 +**核心权衡**: +- **收益**:释放访问槽位 → 可服务更多需求 +- **成本**:需求不确定性 → 可能导致某站点服务不足 --- -## 2. 符号定义与数据准备 +## 2. 符号定义与参数估计 ### 2.1 符号体系 -| 符号 | 定义 | 单位/范围 | +| 符号 | 定义 | 来源/估计 | |------|------|----------| -| $\mu_i, \sigma_i$ | 站点 $i$ 的需求均值和标准差 | 户 | -| $\tilde{\mu}_i$ | 截断修正后的真实需求(来自Task 1) | 户 | +| $\mu_i, \sigma_i$ | 站点 $i$ 的需求均值和标准差 | 2019年历史数据 | +| $\tilde{\mu}_i$ | 截断修正后的真实需求 | Task 1截断回归 | | $D_i$ | 站点 $i$ 的随机需求 | $D_i \sim N(\mu_i, \sigma_i^2)$ | -| $Q$ | 卡车单次总服务能力 | 户(≈400,基于15000 lbs) | -| $q$ | 第一站点预分配量 | 户 | -| $l_{ij}$ | 站点 $i, j$ 间行驶距离 | 英里 | -| $T_{travel}$ | 站点间转场时间 | 小时 | -| $T_{service}$ | 单站点服务时间(≈2小时) | 小时 | +| $Q$ | 卡车单次总服务能力 | 400户 | +| $q$ | 第一站点预分配量 | **决策变量** | +| $l_{ij}$ | 站点间距离 | Haversine公式计算 | +| $k_i$ | 站点 $i$ 的年度访问次数 | Task 1 Hamilton分配 | -### 2.2 关键参数估计 +### 2.2 关键参数推导 -**卡车容量转换**: -$$Q = \frac{15000 \text{ lbs}}{w} \approx \frac{15000}{40} = 375 \text{ 户}$$ +**为什么 $Q = 400$?** -其中 $w \approx 40$ lbs/户(基于典型服务225户 × 15000/225 ≈ 67 lbs,取保守估计)。 +题面信息: +- 卡车运力 = 15,000 lbs +- 典型服务 = 200-250户 -考虑到 $\mu_{max} = 396.6$,取 $Q = 400$ 作为软上限。 +推导: +$$\text{每户食物量} = \frac{15000}{225} \approx 67 \text{ lbs}$$ + +但数据显示 $\mu_{max} = 396.6$,说明实际每户分配可能更少: +$$\text{实际每户} \approx \frac{15000}{400} = 37.5 \text{ lbs}$$ + +取 $Q = 400$ 作为容量上限,与数据一致。 --- -## 3. 共生站点配对选择模型 +## 3. 共生站点配对模型 -### 3.1 配对可行性约束 +### 3.1 配对约束的底层逻辑 **约束1:同日可达性** -单日时间预算:$T_{day} = 8$ 小时(工作日) +**为什么需要距离约束?** -可行条件: -$$2 \times T_{service} + T_{travel}(l_{ij}) \leq T_{day}$$ -$$T_{travel}(l_{ij}) = \frac{l_{ij}}{v_{avg}} + T_{setup}$$ +单日时间预算分析: +``` +工作日时间 = 8小时 +单站点服务 = 2小时 × 2 = 4小时 +转场+准备 = 0.5 + 行驶时间 +可用行驶时间 = 8 - 4 - 0.5 = 3.5小时 +``` -其中 $v_{avg} \approx 30$ mph(含路况因素),$T_{setup} = 0.5$ 小时(卸货准备)。 +假设平均车速30 mph(含路况): +$$l_{max} = 3.5 \times 30 = 105 \text{ miles}$$ -**距离约束**: -$$l_{ij} \leq (T_{day} - 2 \times T_{service} - T_{setup}) \times v_{avg} = (8 - 4 - 0.5) \times 30 = 105 \text{ miles}$$ +**为什么取50英里而非105英里?** +- 保守设计:留有余量应对交通延误 +- 敏感性分析显示:50英里已覆盖足够多的配对 -取保守阈值 $l_{max} = 50$ 英里,确保充裕的服务时间。 +**距离计算公式**(Haversine简化): +$$l_{ij} = 69.0 \times \sqrt{(\Delta lat)^2 + \cos^2(lat_{avg})(\Delta lon)^2}$$ -**距离计算**(Haversine公式简化): -$$l_{ij} = 69.0 \times \sqrt{(lat_i - lat_j)^2 + \cos^2\left(\frac{lat_i + lat_j}{2} \times \frac{\pi}{180}\right)(lon_i - lon_j)^2}$$ +- 69.0 = 1度纬度对应的英里数 +- $\cos^2(lat_{avg})$ = 经度修正因子(高纬度地区经度圈变小) **约束2:容量可行性** -$$\mu_i + \mu_j \leq Q + \delta$$ +**为什么 $\mu_i + \mu_j \leq 450$?** -取 $\delta = 50$(允许10%溢出,由需求波动吸收),即: -$$\mu_i + \mu_j \leq 450$$ +- 卡车容量 $Q = 400$ +- 允许10%溢出($\delta = 50$):因为需求是随机的,平均值之和略超容量时,实际服务可能仍在容量内 + +$$\mu_i + \mu_j \leq Q + \delta = 450$$ **约束3:需求稳定性** -变异系数约束(排除高波动配对): -$$CV_i = \frac{\sigma_i}{\mu_i} \leq 0.5 \quad \text{且} \quad CV_j = \frac{\sigma_j}{\mu_j} \leq 0.5$$ +**为什么限制CV(变异系数)?** -### 3.2 配对价值评估 +变异系数 $CV = \sigma / \mu$ 衡量需求的相对波动性。 -对于满足可行性约束的配对 $(i, j)$,定义**配对价值函数**: +高CV站点的问题: +- 需求高度不确定 +- 分配策略难以优化 +- 服务缺口风险高 -$$V_{ij} = \underbrace{\alpha \cdot \frac{\mu_i + \mu_j}{Q}}_{\text{容量利用率}} - \underbrace{\beta \cdot \frac{l_{ij}}{l_{max}}}_{\text{距离惩罚}} - \underbrace{\gamma \cdot \frac{\sigma_i^2 + \sigma_j^2}{(\mu_i + \mu_j)^2}}_{\text{风险惩罚}} + \underbrace{\delta \cdot \mathbb{1}[\rho_{ij} < 0]}_{\text{对冲奖励}}$$ +经验阈值:$CV_{max} = 0.5$(即标准差不超过均值的一半) -其中: -- $\alpha = 1.0$:容量利用权重 -- $\beta = 0.3$:距离惩罚权重 -- $\gamma = 0.5$:风险惩罚权重 -- $\delta = 0.2$:需求负相关奖励 -- $\rho_{ij}$:需求相关系数(若数据可得) +### 3.2 配对价值函数的设计逻辑 -### 3.3 配对选择算法 +**为什么需要价值函数?** -``` -Algorithm 1: Symbiotic Site Pairing -──────────────────────────────────── -Input: 70 sites with (μᵢ, σᵢ, latᵢ, lonᵢ) -Output: Set of paired sites P +满足约束的配对可能有上千个(实际1568对),需要选择最优的子集。 -1. Compute pairwise distance matrix L[i,j] -2. Initialize candidate set C = ∅ -3. For each pair (i,j) where i < j: - a. If l_ij ≤ l_max AND μᵢ + μⱼ ≤ 450 AND CVᵢ ≤ 0.5 AND CVⱼ ≤ 0.5: - Add (i,j) to C with value V_ij -4. Sort C by V_ij descending -5. Initialize P = ∅, used = ∅ -6. For each (i,j) in C: - If i ∉ used AND j ∉ used: - Add (i,j) to P - Add i, j to used -7. Return P -``` +**价值函数设计**: -**贪心策略的合理性**:高价值配对优先,避免一个站点被多次配对。 +$$V_{ij} = \underbrace{\alpha \cdot \frac{\mu_i + \mu_j}{Q}}_{\text{容量利用率}} - \underbrace{\beta \cdot \frac{l_{ij}}{l_{max}}}_{\text{距离惩罚}} - \underbrace{\gamma \cdot \frac{\sigma_i^2 + \sigma_j^2}{(\mu_i + \mu_j)^2}}_{\text{风险惩罚}}$$ + +**各项解释**: + +| 项 | 逻辑 | 权重 | +|----|------|------| +| 容量利用率 | 需求和接近Q的配对更有价值 | $\alpha = 1.0$ | +| 距离惩罚 | 距离远的配对效率低 | $\beta = 0.3$ | +| 风险惩罚 | 波动大的配对风险高 | $\gamma = 0.5$ | + +**为什么用贪心算法而非整数规划?** + +- 贪心算法:O(n²),实现简单,结果接近最优 +- 整数规划:精确但复杂度高,收益有限 +- 约束"每站点最多配对一次"使贪心算法非常有效 + +### 3.3 实际运行结果 + +**配对筛选**: +- 候选配对数:1568对(满足3个约束) +- 最终选择:**34对**(覆盖68个站点,97%) +- 未配对站点:2个 + +**Top 10 高价值配对**: + +| 配对 | 站点i | 站点j | 距离 | 需求和 | 价值 | +|------|-------|-------|------|--------|------| +| 1 | Boys and Girls Club | Redeemer Lutheran | 0.4 mi | 441 | 1.07 | +| 2 | Rathbone | Woodhull | 5.8 mi | 445 | 1.06 | +| 3 | Bath | Campbell | 9.6 mi | 448 | 1.05 | +| 4 | Springview Apts | Waverly | 0.8 mi | 424 | 1.05 | +| 5 | Birnie Transport | Lindley | 10.1 mi | 446 | 1.04 | --- @@ -138,350 +334,331 @@ Output: Set of paired sites P ### 4.1 问题形式化 -设卡车访问配对 $(i, j)$,先访问站点 $i$,后访问站点 $j$。 +**为什么建模为随机优化?** -**随机变量**: -- $D_i \sim N(\mu_i, \sigma_i^2)$:站点 $i$ 的实际需求 -- $D_j \sim N(\mu_j, \sigma_j^2)$:站点 $j$ 的实际需求 +确定性思维的错误: +> "给站点i分配 $\mu_i$,给站点j分配 $Q - \mu_i$" -**决策变量**:$q$ = 为站点 $i$ 预留的食物量(在出发时决定) +问题:实际需求 $D_i$ 可能大于或小于 $\mu_i$: +- 若 $D_i > \mu_i$:站点i服务不足 +- 若 $D_i < \mu_i$:多余食物浪费在站点i,站点j也可能不足 -**服务量**: -- 站点 $i$ 实际服务量:$S_i = \min(D_i, q)$ -- 站点 $j$ 实际服务量:$S_j = \min(D_j, Q - S_i) = \min(D_j, Q - \min(D_i, q))$ +**正确的建模**: -### 4.2 目标函数设计 +决策变量:$q$ = 为站点i预留的食物量 -**目标1:最大化期望总服务量** +服务量: +$$S_i = \min(D_i, q), \quad S_j = \min(D_j, Q - S_i)$$ -$$\max_q \quad E[S_i + S_j] = E[\min(D_i, q)] + E[\min(D_j, Q - \min(D_i, q))]$$ +目标:最大化期望总服务量 +$$\max_q \quad E[S_i + S_j]$$ -**目标2:考虑公平性的惩罚项** +### 4.2 解析解的推导过程 -定义未满足需求: -$$U_i = (D_i - q)^+ = \max(0, D_i - q)$$ -$$U_j = (D_j - (Q - S_i))^+$$ +**引理:截断正态期望** -**综合目标函数**: -$$\max_q \quad E[S_i + S_j] - \lambda \cdot E[U_i + U_j] - \eta \cdot E[\max(U_i, U_j)]$$ +对于 $X \sim N(\mu, \sigma^2)$: +$$E[\min(X, c)] = \mu \cdot \Phi(z) - \sigma \cdot \phi(z) + c \cdot (1 - \Phi(z))$$ -其中: -- $\lambda$:未满足需求的边际惩罚(取 $\lambda = 0.5$) -- $\eta$:最大缺口惩罚,体现公平性(取 $\eta = 0.3$) +其中 $z = \frac{c - \mu}{\sigma}$。 -### 4.3 解析解推导 +**推导**: -**引理(截断正态期望)**:若 $X \sim N(\mu, \sigma^2)$,则: -$$E[\min(X, c)] = \mu \cdot \Phi(z) + \sigma \cdot \phi(z) - c \cdot (1 - \Phi(z))$$ -其中 $z = \frac{c - \mu}{\sigma}$,$\Phi, \phi$ 为标准正态CDF和PDF。 +$$E[\min(X, c)] = \int_{-\infty}^{c} x \cdot f(x) dx + c \cdot P(X > c)$$ -**简化情形**:假设 $D_i, D_j$ 独立,忽略 $\min(D_i, q)$ 的随机性对第二项的影响。 +第一项通过分部积分,第二项直接计算,得到上述公式。 -**一阶近似**: -$$E[S_i] \approx \mu_i \cdot \Phi\left(\frac{q - \mu_i}{\sigma_i}\right) + \sigma_i \cdot \phi\left(\frac{q - \mu_i}{\sigma_i}\right)$$ +**最优条件推导**: -$$E[S_j] \approx \mu_j \cdot \Phi\left(\frac{Q - q - \mu_j}{\sigma_j}\right) + \sigma_j \cdot \phi\left(\frac{Q - q - \mu_j}{\sigma_j}\right)$$ +简化假设:$D_i, D_j$ 独立,忽略 $S_i$ 的随机性对 $E[S_j]$ 的影响。 -**最优条件**(对 $q$ 求导并令其为零): +$$E[S_i] \approx g(q; \mu_i, \sigma_i)$$ +$$E[S_j] \approx g(Q - q; \mu_j, \sigma_j)$$ -$$\frac{\partial E[S_i]}{\partial q} = \frac{\partial E[S_j]}{\partial q}$$ +对 $q$ 求导并令其为零: +$$\frac{\partial E[S_i]}{\partial q} = \frac{\partial E[S_j]}{\partial (-q)}$$ -化简得: -$$\Phi\left(\frac{q^* - \mu_i}{\sigma_i}\right) = \Phi\left(\frac{Q - q^* - \mu_j}{\sigma_j}\right)$$ +利用截断正态的导数性质: +$$\frac{\partial E[\min(X, c)]}{\partial c} = 1 - \Phi\left(\frac{c - \mu}{\sigma}\right)$$ -**最优分配公式**: +得到: +$$1 - \Phi\left(\frac{q - \mu_i}{\sigma_i}\right) = 1 - \Phi\left(\frac{Q - q - \mu_j}{\sigma_j}\right)$$ -$$q^* = \frac{\sigma_j \mu_i + \sigma_i \mu_j + \sigma_i (Q - \mu_j) - \sigma_j \mu_i}{\sigma_i + \sigma_j} = \frac{\sigma_j \mu_i + \sigma_i (Q - \mu_j)}{\sigma_i + \sigma_j}$$ +$$\Phi\left(\frac{q - \mu_i}{\sigma_i}\right) = \Phi\left(\frac{Q - q - \mu_j}{\sigma_j}\right)$$ -整理得: -$$\boxed{q^* = \frac{\sigma_j}{\sigma_i + \sigma_j} \mu_i + \frac{\sigma_i}{\sigma_i + \sigma_j} (Q - \mu_j)}$$ +由 $\Phi$ 的单调性: +$$\frac{q - \mu_i}{\sigma_i} = \frac{Q - q - \mu_j}{\sigma_j}$$ -**物理解释**: -- 当 $\sigma_j \gg \sigma_i$(站点 $j$ 波动大):$q^* \to \mu_i$,为站点 $i$ 精确分配 -- 当 $\sigma_i \gg \sigma_j$(站点 $i$ 波动大):$q^* \to Q - \mu_j$,为站点 $j$ 预留更多 -- 波动大的站点需要更多"缓冲" +解得: +$$\boxed{q^* = \frac{\sigma_j \mu_i + \sigma_i (Q - \mu_j)}{\sigma_i + \sigma_j}}$$ -### 4.4 鲁棒性考虑 +### 4.3 最优分配公式的物理解释 -**最坏情况约束**:确保在高需求情景下两站点均获得最低服务 +改写公式: +$$q^* = \frac{\sigma_j}{\sigma_i + \sigma_j} \cdot \mu_i + \frac{\sigma_i}{\sigma_i + \sigma_j} \cdot (Q - \mu_j)$$ -$$q \geq \mu_i - k \sigma_i \quad \text{(站点 $i$ 的下界保护)}$$ -$$Q - q \geq \mu_j - k \sigma_j \quad \text{(站点 $j$ 的下界保护)}$$ +**解释**: +- 当 $\sigma_j \gg \sigma_i$:$q^* \to \mu_i$(为站点i精确分配,因为j波动大需要更多缓冲) +- 当 $\sigma_i \gg \sigma_j$:$q^* \to Q - \mu_j$(为站点j预留更多,因为i波动大) +- 当 $\sigma_i = \sigma_j$:$q^* = \frac{\mu_i + Q - \mu_j}{2}$(平均分配) -取 $k = 1$(约84%保护水平),则: -$$\mu_i - \sigma_i \leq q \leq Q - \mu_j + \sigma_j$$ +**核心洞察**:波动大的站点需要更多"缓冲"空间。 -**最终分配策略**: -$$q^{final} = \text{clip}(q^*, \mu_i - \sigma_i, Q - \mu_j + \sigma_j)$$ +### 4.4 鲁棒性约束 + +**为什么需要约束?** + +极端情况下,$q^*$ 可能导致某站点获得很少的预留: +- 若 $q^* < \mu_i - \sigma_i$:站点i很可能服务不足 +- 若 $Q - q^* < \mu_j - \sigma_j$:站点j很可能服务不足 + +**约束设计**: +$$\mu_i - k\sigma_i \leq q \leq Q - \mu_j + k\sigma_j$$ + +取 $k = 1$(约84%保护水平)。 + +**实际效果**:34对配对中,无一触及边界约束——说明最优解本身已经是鲁棒的。 + +### 4.5 实际分配结果 + +| 统计量 | 值 | +|--------|-----| +| $q^*$ 范围 | [23.9, 315.6] | +| 分配比例范围 | [6.0%, 78.9%] | +| 平均分配比例 | 50.6% | +| 平均效率 | 94.2% | + +**Top 5 配对的分配方案**: + +| 配对 | $\mu_i$ | $\mu_j$ | $\sigma_i$ | $\sigma_j$ | $q^*$ | 比例 | +|------|---------|---------|------------|------------|-------|------| +| Boys & Girls + Redeemer | 210.8 | 230.6 | 28.9 | 93.5 | 195.9 | 49.0% | +| Rathbone + Woodhull | 269.1 | 176.0 | 38.2 | 35.9 | 248.1 | 62.0% | +| Bath + Campbell | 279.5 | 168.5 | 67.9 | 33.9 | 254.3 | 63.6% | +| Springview + Waverly | 27.6 | 396.6 | 5.6 | 51.9 | 23.9 | 6.0% | +| Birnie + Lindley | 213.4 | 232.9 | 27.8 | 56.9 | 189.4 | 47.3% | --- ## 5. 全局调度优化 -### 5.1 双站点访问的引入策略 +### 5.1 合并比例的设计逻辑 -**问题**:在Task 1的730次单站点访问中,哪些应改为双站点访问? +**为什么不全部合并?** -**决策逻辑**: -1. 对于配对 $(i, j) \in P$,合并其部分访问 -2. 设原频次为 $k_i, k_j$,合并后: - - 双站点访问次数:$k_{ij} = \min(k_i, k_j, k_{max})$ - - 站点 $i$ 剩余单独访问:$k_i' = k_i - k_{ij}$ - - 站点 $j$ 剩余单独访问:$k_j' = k_j - k_{ij}$ +风险考虑: +- 双站点访问有服务缺口风险 +- 保留部分单独访问作为"保险" -取 $k_{max} = \lfloor \min(k_i, k_j) / 2 \rfloor$,确保每个站点仍保留独立访问。 +**合并比例公式**: +$$k_{ij} = \lfloor \min(k_i, k_j) / 2 \rfloor$$ -**释放的访问次数**: -$$\Delta N = \sum_{(i,j) \in P} k_{ij}$$ +**示例**:站点i有10次访问,站点j有6次 +- 双站点访问次数:$\lfloor \min(10, 6) / 2 \rfloor = 3$ +- 站点i剩余单独访问:$10 - 3 = 7$ +- 站点j剩余单独访问:$6 - 3 = 3$ -这些次数可重新分配给其他高需求站点。 +### 5.2 释放槽位的重分配 -### 5.2 频次重分配模型 +**为什么双站点访问算1次事件?** -设释放 $\Delta N$ 次访问,按需求比例分配给非配对站点: +题目说"sending the same truck to visit two sites on **some of the trips**"——双站点是一次"行程"。 -$$k_i^{new} = k_i + \Delta N \cdot \frac{\tilde{\mu}_i}{\sum_{l \notin P} \tilde{\mu}_l} \quad \text{for } i \notin P$$ +**重分配逻辑**: +1. 计算释放的槽位:$\Delta N = \sum k_{ij} = 142$ +2. 按需求比例分配给所有站点 +3. 使用Hamilton方法取整 -使用Hamilton方法取整(与Task 1一致)。 +### 5.3 实际运行结果 -### 5.3 日历排程调整 - -**约束修改**: -- 原约束:每日恰好2个单站点访问 -- 新约束:每日恰好2个"访问事件"(单站点或双站点) - -**双站点排程优先级**: -- 双站点访问消耗更多时间,优先安排在周中(避免周末交通) -- 同一配对的多次访问应均匀分布 - -**排程算法**:沿用Task 1的贪心算法,将双站点访问视为单一事件。 +| 指标 | 值 | +|------|-----| +| 配对数 | 34 | +| 双站点访问次数 | 142 | +| 释放槽位 | 142 | +| 最终单站点访问 | 588 | +| 最终总事件 | 730(符合约束)| --- -## 6. 效果评估体系 +## 6. 效果评估 -### 6.1 有效性指标扩展 +### 6.1 指标定义与逻辑 **E1':期望总服务量** -$$E_1' = \sum_{i \notin P} k_i' \cdot \mu_i + \sum_{(i,j) \in P} k_{ij} \cdot E[S_i + S_j] + \sum_{(i,j) \in P} (k_i' \cdot \mu_i + k_j' \cdot \mu_j)$$ +$$E_1' = \sum_{\text{单站点}} k_i \cdot \mu_i + \sum_{\text{双站点}} k_{ij} \cdot E[S_i + S_j]$$ -**E2':质量加权服务量**(考虑双站点服务质量折扣) +**E2':质量加权服务量** -双站点访问的质量因子: -$$q_{ij} = \frac{Q}{E[S_i + S_j]} \cdot q(\mu_i) \cdot q(\mu_j)$$ +**为什么需要质量加权?** -### 6.2 公平性指标扩展 +当服务户数超过250时,每户分得的食物减少: +$$q(\mu) = \min\left(1, \frac{250}{\mu}\right)$$ -**F1':满足率Gini系数** +**双站点的质量计算(总量法)**: +$$q_{ij} = \min\left(1, \frac{250}{\mu_i + \mu_j}\right)$$ -$$r_i = \frac{\text{年度期望服务量}_i}{\tilde{\mu}_i \times (\text{年度需求发生次数})}$$ +**为什么用总量而非分站点计算?** +- 双站点共享同一卡车的食物 +- 总负载决定每户分得的量 +- 更符合物理实际 -**F2':最低满足率** +**满足率 $r_i$**(与Task 1定义一致): +$$r_i = \frac{k_i \cdot \mu_i}{\tilde{\mu}_i}$$ -$$F_2' = \min_i r_i$$ +对于参与配对的站点: +$$r_i = \frac{k_i^{single} \cdot \mu_i + k_{ij} \cdot E[S_i]}{\tilde{\mu}_i}$$ -### 6.3 风险指标(新增) +**R1:服务缺口风险** -**R1:服务缺口概率** +$$R_1 = P(S_i / D_i < 0.8 \text{ 或 } S_j / D_j < 0.8)$$ -$$R_1 = P(\text{至少一个站点服务量} < 0.8 \times \text{需求})$$ +### 6.2 实际结果对比 -**R2:期望最大缺口** +| 指标 | Task 1 | Task 3 | 变化 | 变化% | +|------|--------|--------|------|-------| +| **E1 (总服务量)** | 140,121 | 163,777 | +23,656 | **+16.9%** | +| **E2 (质量加权)** | 131,673 | 138,699 | +7,026 | **+5.3%** | +| F1 (Gini系数) | 0.314 | 0.320 | +0.006 | +1.9% | +| F2 (最低满足率) | 2.0 | 2.0 | 0 | 0% | +| **R1 (缺口风险)** | 0 | 0.171 | +0.171 | 新增 | +| **RS (资源节省)** | 0% | 19.5% | +19.5% | 新增 | -$$R_2 = E[\max(U_i, U_j)]$$ - -### 6.4 资源效率指标(新增) - -**Efficiency Gain**: -$$\text{EG} = \frac{E_1' - E_1^{Task1}}{E_1^{Task1}} \times 100\%$$ - -**Resource Saving**: -$$\text{RS} = \frac{\Delta N}{730} \times 100\%$$ +**核心发现**: +1. 通过双站点模式,释放19.5%的访问槽位 +2. 总服务量提升16.9% +3. 公平性几乎不变(Gini仅增加1.9%) +4. 代价是引入17.1%的服务缺口风险 --- -## 7. 数值算例与结果 +## 7. 敏感性分析 -### 7.1 配对筛选结果 +### 7.1 分析参数 -基于70个站点数据,应用Algorithm 1: +| 参数 | 符号 | 基准值 | 扫描范围 | +|------|------|--------|---------| +| 合并比例 | $r_{merge}$ | 1/2 | [1/3, 1/2, 2/3] | +| 距离阈值 | $l_{max}$ | 50 mi | [30, 40, 50, 60, 70] | +| 容量上限 | $\mu_{sum,max}$ | 450 | [400, 425, 450, 475, 500] | +| CV阈值 | $CV_{max}$ | 0.5 | [0.3, 0.4, 0.5, 0.6] | -| 配对编号 | 站点 $i$ | 站点 $j$ | $l_{ij}$ (mi) | $\mu_i + \mu_j$ | $V_{ij}$ | -|---------|---------|---------|--------------|-----------------|----------| -| 1 | MFP Owego | MFP Candor | 12.3 | 287 | 0.82 | -| 2 | MFP Elmira | MFP Horseheads | 8.7 | 312 | 0.79 | -| 3 | MFP Norwich | MFP Oxford | 15.1 | 245 | 0.71 | -| ... | ... | ... | ... | ... | ... | +### 7.2 敏感性结果 -**预计配对数量**:10-15对(具体取决于数据) +**1. 合并比例 — 最敏感参数** -### 7.2 分配策略示例 +| merge_ratio | 双站点次数 | E1 | E2 | F1 | R1 | +|-------------|-----------|-----|-----|------|------| +| **1/3 (0.333)** | 93 | 155,724 | 136,556 | 0.3200 | 0.1708 | +| **1/2 (0.500)** | 142 | 163,777 | 138,699 | 0.3199 | 0.1708 | +| **2/3 (0.667)** | 196 | 171,574 | 140,891 | 0.3122 | 0.1708 | -以配对 (MFP Owego, MFP Candor) 为例: -- $\mu_i = 156, \sigma_i = 42$ -- $\mu_j = 131, \sigma_j = 38$ -- $Q = 400$ +- **E1变化幅度:9.68%** — 最敏感 +- 提高合并比例可显著增加服务量,但可能略微降低公平性(F1)。 -$$q^* = \frac{38 \times 156 + 42 \times (400 - 131)}{42 + 38} = \frac{5928 + 11298}{80} = 215$$ +**2. 距离阈值 — 稳健** -**分配方案**:第一站点预留215户食物量,剩余185户给第二站点。 +| l_max | 配对数 | E1 | E2 | F1 | R1 | +|-------|--------|-----|-----|------|------| +| 30 mi | 33 | 162,180 | 137,436 | 0.3173 | 0.1757 | +| 40 mi | 34 | 163,252 | 138,368 | 0.3201 | 0.1714 | +| 50 mi | 34 | 163,777 | 138,699 | 0.3199 | 0.1708 | +| 60 mi | 34 | 163,763 | 138,587 | 0.3200 | 0.1714 | +| 70 mi | 34 | 163,588 | 138,484 | 0.3201 | 0.1714 | -### 7.3 效果对比 +- **E1变化幅度:0.98%** — 模型极其稳健 +- 50英里是平衡效率与计算复杂度的理想选择。 -| 指标 | Task 1方案 | Task 3方案 | 变化 | -|------|-----------|-----------|------| -| E1 (总服务量) | 140,121 | 144,500 | +3.1% | -| E2 (质量加权) | 131,673 | 134,200 | +1.9% | -| F1 (Gini) | 0.314 | 0.298 | -5.1% (改善) | -| F2 (min满足率) | 2.00 | 2.15 | +7.5% | -| R1 (缺口概率) | 0 | 0.12 | 新增风险 | -| RS (资源节省) | 0% | 4.1% | 30次访问 | +**3. 容量上限 — 风险敏感** + +| mu_sum_max | E1 | E2 | F1 | R1 | +|------------|-----|-----|------|------| +| 400 | 162,858 | 140,271 | 0.3227 | 0.0937 | +| 425 | 163,146 | 139,306 | 0.3213 | 0.1514 | +| 450 | 163,777 | 138,699 | 0.3199 | 0.1708 | +| 475 | 163,153 | 138,113 | 0.3185 | 0.1894 | +| 500 | 162,688 | 136,835 | 0.3123 | 0.2036 | + +- **R1变化幅度:10.99%** — 对风险影响极大 +- 较低容量上限(400)可将风险降低近一半,同时维持较高的质量加权服务量(E2)。 + +**4. CV阈值 — 准入敏感** + +| cv_max | 配对数 | E1 | E2 | F1 | R1 | +|--------|--------|-----|-----|------|------| +| 0.3 | 25 | 157,907 | 135,824 | 0.3190 | 0.1810 | +| 0.4 | 31 | 161,248 | 137,282 | 0.3188 | 0.1748 | +| 0.5 | 34 | 163,777 | 138,699 | 0.3199 | 0.1708 | +| 0.6 | 35 | 163,951 | 138,873 | 0.3197 | 0.1659 | + +- **E1变化幅度:3.69%** +- 增加CV阈值允许更多高波动性站点配对,略微提升总量但需谨慎对待。 + +### 7.3 敏感性结论与汇总 + +| 参数 | E1影响范围 | E2影响范围 | R1影响范围 | 结论 | +|------|-----------|-----------|-----------|------| +| **merge_ratio** | 9.68% | 3.13% | 0.00% | ⚠️ 关键决策参数 | +| **l_max** | 0.98% | 0.91% | 0.49% | ✅ 参数稳健 | +| **mu_sum_max** | 0.66% | 2.48% | 10.99% | ⚠️ 风险控制参数 | +| **cv_max** | 3.69% | 2.20% | 1.51% | ✅ 较稳健 | + +**建议**: +1. **合并比例**是最敏感参数,需根据风险承受能力选择 +2. **距离阈值**和**CV阈值**影响有限,模型稳健 +3. **容量上限**影响风险水平,保守选择400可降低风险50% --- -## 8. 敏感性与鲁棒性分析 +## 8. 结果验证 -### 8.1 参数敏感性 +### 8.1 约束满足检验 -| 参数 | 基准值 | 扫描范围 | 对E1'的影响 | -|------|--------|---------|------------| -| $l_{max}$ | 50 mi | [30, 70] | ±1.2% | -| $\lambda$ (惩罚) | 0.5 | [0.3, 0.7] | ±0.8% | -| $k_{max}$ | $\min(k_i,k_j)/2$ | [1/3, 2/3] | ±2.1% | +| 检验项 | 要求 | 实际 | 结果 | +|--------|------|------|------| +| 每日访问事件数 | 2 | min=2, max=2 | ✅ 通过 | +| 年度总事件数 | 730 | 730 | ✅ 通过 | +| 站点覆盖 | 全覆盖 | 70/70 | ✅ 通过 | +| $q^*$边界检查 | 在[q_lower, q_upper]内 | 34/34在边界内 | ✅ 通过 | -### 8.2 场景分析 +### 8.2 模型有效性验证 -**场景1:需求普遍上升10%** -- 配对约束收紧,可行配对数减少 -- 建议:降低配对比例,保留更多单站点访问 +**与Task 1对比**: +- E1提升16.9%:释放的槽位被有效利用 +- F1几乎不变:公平性未受损 +- R1可控:17.1%的缺口风险在合理范围 -**场景2:需求波动增大(σ增加20%)** -- 风险指标R1显著上升 -- 建议:收紧稳定性约束 $CV_{max} = 0.4$ +**物理合理性**: +- 高价值配对的需求和接近容量(平均413) +- 最优分配比例接近50%(平均50.6%) +- 低需求配对的双站点次数较少(合理) --- -## 9. 算法实现 +## 9. 待实现:可视化图表 -### 9.1 Algorithm 2: First-Site Allocation Strategy +### 9.1 图表清单 -```python -def optimal_allocation(mu_i, sigma_i, mu_j, sigma_j, Q=400): - """ - 计算双站点访问时第一站点的最优分配量 +| 图编号 | 图名 | 内容 | 目的 | +|--------|------|------|------| +| Fig.1 | 站点配对地图 | 70站点+34条配对连线 | 展示空间分布 | +| Fig.2 | 分配策略散点图 | q* vs (μ_i, σ_i, σ_j) | 验证分配逻辑 | +| Fig.3 | 敏感性曲线 | 4参数对E1,E2,R1的影响 | 参数选择依据 | +| Fig.4 | 日历热力图 | 365天×2槽位 | 排程可视化 | +| Fig.5 | 风险分布图 | 34对的缺口概率分布 | 风险识别 | - Parameters: - ----------- - mu_i, sigma_i : float - 第一站点的需求均值和标准差 - mu_j, sigma_j : float - 第二站点的需求均值和标准差 - Q : float - 卡车总容量(以户数计) +### 9.2 可视化脚本(待实现) - Returns: - -------- - q_final : float - 第一站点的最优分配量 - """ - # 计算最优分配 - q_star = (sigma_j * mu_i + sigma_i * (Q - mu_j)) / (sigma_i + sigma_j) - - # 应用鲁棒性边界 - q_lower = max(0, mu_i - sigma_i) - q_upper = min(Q, Q - mu_j + sigma_j) - - # 裁剪到可行范围 - q_final = max(q_lower, min(q_star, q_upper)) - - return q_final ``` - -### 9.2 Algorithm 3: Integrated Scheduling - -```python -def integrated_scheduling(sites, pairings, original_k, Q=400): - """ - 整合双站点访问的全局调度 - - Parameters: - ----------- - sites : DataFrame - 站点数据,包含 mu, sigma, lat, lon - pairings : list of tuples - 配对站点列表 [(i, j), ...] - original_k : dict - 原始访问次数分配 {site_id: k} - - Returns: - -------- - schedule : DataFrame - 完整的年度调度表 - """ - paired_sites = set() - for i, j in pairings: - paired_sites.add(i) - paired_sites.add(j) - - # 步骤1:计算每个配对的合并访问次数 - dual_visits = {} - single_visits = original_k.copy() - - for i, j in pairings: - k_ij = min(original_k[i], original_k[j]) // 2 - dual_visits[(i, j)] = k_ij - single_visits[i] = original_k[i] - k_ij - single_visits[j] = original_k[j] - k_ij - - # 步骤2:计算释放的访问次数 - delta_N = sum(dual_visits.values()) - - # 步骤3:重分配给非配对站点 - non_paired_demand = sum(sites.loc[i, 'mu_corrected'] - for i in sites.index if i not in paired_sites) - - for i in sites.index: - if i not in paired_sites: - extra = int(delta_N * sites.loc[i, 'mu_corrected'] / non_paired_demand) - single_visits[i] += extra - - # 步骤4:生成访问事件列表 - events = [] - - # 单站点访问 - for site_id, k in single_visits.items(): - for visit_num in range(k): - ideal_day = (visit_num + 0.5) * 365 / k - events.append({ - 'type': 'single', - 'site_i': site_id, - 'site_j': None, - 'ideal_day': ideal_day, - 'q': None - }) - - # 双站点访问 - for (i, j), k_ij in dual_visits.items(): - mu_i, sigma_i = sites.loc[i, ['mu', 'sigma']] - mu_j, sigma_j = sites.loc[j, ['mu', 'sigma']] - q_opt = optimal_allocation(mu_i, sigma_i, mu_j, sigma_j, Q) - - for visit_num in range(k_ij): - ideal_day = (visit_num + 0.5) * 365 / k_ij - events.append({ - 'type': 'dual', - 'site_i': i, - 'site_j': j, - 'ideal_day': ideal_day, - 'q': q_opt - }) - - # 步骤5:贪心排程(与Task 1相同) - events.sort(key=lambda x: x['ideal_day']) - schedule = greedy_schedule(events, days=365, slots_per_day=2) - - return schedule +task3/ +├── 08_visualize.py # 可视化脚本 +└── figures/ + ├── fig1_pairing_map.png + ├── fig2_allocation_scatter.png + ├── fig3_sensitivity.png + ├── fig4_calendar_heatmap.png + └── fig5_risk_distribution.png ``` --- @@ -490,253 +667,95 @@ def integrated_scheduling(sites, pairings, original_k, Q=400): ### 10.1 主要发现 -1. **配对收益**:双站点模式可节省约4%的访问次数,释放资源服务更多需求 +1. **配对收益**:双站点模式可节省19.5%的访问槽位,服务量提升16.9% 2. **分配策略**:最优分配量 $q^*$ 与两站点需求波动性成反比——波动大的站点需要更多"缓冲" -3. **风险可控**:通过鲁棒性约束,服务缺口概率控制在12%以内 +3. **风险可控**:通过合理的配对选择和鲁棒性约束,服务缺口风险控制在17%以内 +4. **参数稳健**:除合并比例外,其他参数对结果影响有限 ### 10.2 对FBST的建议 | 建议 | 理由 | |------|------| +| 采用双站点模式 | 可释放19.5%资源服务更多需求 | | 优先配对低需求、地理相近站点 | 容量利用率高,风险低 | | 保留高需求站点的单独访问 | 避免服务不足 | | 记录双站点访问的实际服务量 | 校准模型参数 | -| 建立动态调整机制 | 季度复盘,调整配对策略 | +| 合并比例取1/2 | 平衡效率与风险 | ### 10.3 模型局限性 1. **需求独立性假设**:实际中相邻站点需求可能相关 2. **服务时间固定**:大需求站点可能需要更长服务时间 3. **天气因素未纳入**:可与Task 2结合考虑 - ---- - -## 11. 流程图 - -### 11.1 Mermaid版本 - -```mermaid -flowchart TB - subgraph INPUT["数据输入"] - A[Task 1 结果
70站点 + 730次分配] - end - - subgraph TASK3["TASK 3: 双站点同车优化"] - direction TB - - subgraph PAIRING["阶段1: 配对选择"] - B1[距离矩阵计算] - B2[可行性筛选
距离/容量/稳定性] - B3[配对价值评估
V_ij计算] - B4[贪心配对选择] - B1 --> B2 --> B3 --> B4 - end - - subgraph ALLOCATION["阶段2: 分配优化"] - C1[随机优化建模] - C2[解析解推导
q* 公式] - C3[鲁棒性约束] - C1 --> C2 --> C3 - end - - subgraph SCHEDULING["阶段3: 全局调度"] - D1[访问次数重分配] - D2[日历排程调整] - D1 --> D2 - end - - subgraph EVALUATION["阶段4: 效果评估"] - E1[有效性 E1', E2'] - E2[公平性 F1', F2'] - E3[风险 R1, R2] - E4[资源节省 RS] - end - - PAIRING --> ALLOCATION - ALLOCATION --> SCHEDULING - SCHEDULING --> EVALUATION - end - - subgraph OUTPUT["输出"] - F1[共生站点配对表] - F2[最优分配策略] - F3[2021年双站点调度表] - F4[效果对比报告] - end - - A --> B1 - EVALUATION --> F1 - EVALUATION --> F2 - EVALUATION --> F3 - EVALUATION --> F4 - - style PAIRING fill:#87CEEB - style ALLOCATION fill:#90EE90 - style SCHEDULING fill:#FFB6C1 - style EVALUATION fill:#DDA0DD -``` - -### 11.2 ASCII版本 - -``` -┌─────────────────────────────────────────────────────────────────────────────┐ -│ TASK 3: 双站点同车优化 │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ Task 1 结果 ──▶ ┌─────────────────────────────────────────────────────┐ │ -│ (70站点,730次) │ 阶段1: 配对选择 │ │ -│ │ 距离矩阵 → 可行性筛选 → 价值评估 → 贪心选择 │ │ -│ │ (l_ij) (3约束) (V_ij) (Algorithm 1) │ │ -│ └─────────────────────────┬───────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ 阶段2: 分配优化 │ │ -│ │ │ │ -│ │ max E[S_i + S_j] - λE[U_i + U_j] │ │ -│ │ ↓ │ │ -│ │ q* = (σⱼμᵢ + σᵢ(Q-μⱼ)) / (σᵢ+σⱼ) │ │ -│ │ ↓ │ │ -│ │ q_final = clip(q*, μᵢ-σᵢ, Q-μⱼ+σⱼ) │ │ -│ └─────────────────────────┬───────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ 阶段3: 全局调度 │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ 释放 ΔN 次数 │ → │ 重分配给 │ │ │ -│ │ │ (约30次) │ │ 高需求站点 │ │ │ -│ │ └──────────────┘ └──────────────┘ │ │ -│ │ ↓ │ │ -│ │ 日历排程:双站点事件 + 单站点事件 │ │ -│ └─────────────────────────┬───────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ 阶段4: 效果评估 │ │ -│ │ │ │ -│ │ ┌────────────┬────────────┬────────────┐ │ │ -│ │ │ 有效性 │ 公平性 │ 风险 │ │ │ -│ │ │ E1': +3.1% │ F1: -5.1% │ R1: 0.12 │ │ │ -│ │ │ E2': +1.9% │ F2: +7.5% │ RS: 4.1% │ │ │ -│ │ └────────────┴────────────┴────────────┘ │ │ -│ └─────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────┘ -``` +4. **简化的不确定性传播**:忽略了 $S_i$ 对 $S_j$ 的随机影响 --- ## 附录A:关键公式速查 -| 公式 | 用途 | -|------|------| -| $q^* = \frac{\sigma_j \mu_i + \sigma_i (Q - \mu_j)}{\sigma_i + \sigma_j}$ | 最优第一站点分配 | -| $V_{ij} = \alpha \frac{\mu_i + \mu_j}{Q} - \beta \frac{l_{ij}}{l_{max}} - \gamma \frac{\sigma_i^2 + \sigma_j^2}{(\mu_i + \mu_j)^2}$ | 配对价值函数 | -| $l_{ij} = 69.0 \times \sqrt{(\Delta lat)^2 + \cos^2(lat_{avg})(\Delta lon)^2}$ | 距离计算 | -| $E[\min(X, c)] = \mu \Phi(z) + \sigma \phi(z) - c(1-\Phi(z))$ | 截断期望 | -| $R_1 = P(\min(S_i/D_i, S_j/D_j) < 0.8)$ | 服务缺口风险 | +| 公式 | 用途 | 来源 | +|------|------|------| +| $q^* = \frac{\sigma_j \mu_i + \sigma_i (Q - \mu_j)}{\sigma_i + \sigma_j}$ | 最优第一站点分配 | 4.2节推导 | +| $V_{ij} = \alpha \frac{\mu_i + \mu_j}{Q} - \beta \frac{l_{ij}}{l_{max}} - \gamma \frac{\sigma_i^2 + \sigma_j^2}{(\mu_i + \mu_j)^2}$ | 配对价值函数 | 3.2节设计 | +| $E[\min(X, c)] = \mu \Phi(z) - \sigma \phi(z) + c(1-\Phi(z))$ | 截断正态期望 | 统计引理 | +| $q(\mu) = \min(1, 250/\mu)$ | 质量折扣因子 | Task 1定义 | +| $k_{ij} = \lfloor \min(k_i, k_j) / 2 \rfloor$ | 合并次数 | 5.1节设计 | --- -## 附录B:与Task 1的衔接 - -| 维度 | Task 1 | Task 3 | -|------|--------|--------| -| 访问模式 | 单站点 | 单站点 + 双站点 | -| 决策变量 | $k_i$ (访问次数) | $k_i$, $k_{ij}$, $q^*$ | -| 不确定性 | 未考虑 | 核心建模对象 | -| 约束 | 每日2站点 | 每日2"事件" | -| 评估指标 | E1, E2, F1, F2 | E1', E2', F1', F2', R1, R2, RS | - ---- - -## 附录C:参数设置汇总 - -| 参数 | 符号 | 基准值 | 依据 | -|------|------|--------|------| -| 卡车容量 | $Q$ | 400户 | 15000lbs ÷ 40lbs/户 | -| 距离阈值 | $l_{max}$ | 50 mi | 时间预算推导 | -| 容量溢出容忍 | $\delta$ | 50户 | 10%缓冲 | -| CV上限 | $CV_{max}$ | 0.5 | 经验值 | -| 未满足惩罚 | $\lambda$ | 0.5 | 敏感性分析 | -| 最大缺口惩罚 | $\eta$ | 0.3 | 敏感性分析 | -| 鲁棒性水平 | $k$ | 1 | 84%保护 | -| **合并比例** | $r_{merge}$ | 1/2 | 保留50%独立访问 | - ---- - -## 附录D:实现决策记录 - -### D.1 关键设计决策 - -| 决策项 | 选择 | 理由 | -|--------|------|------| -| 数据来源 | Task 1结果 (`task1/03_allocate.xlsx`) | 复用已验证的需求修正和频次分配 | -| 有效性衰减计算 | **总量计算** $q(\mu_i + \mu_j)$ | 双站点共享同一卡车,总负载决定服务质量 | -| 合并比例 | $k_{max} = \lfloor \min(k_i, k_j) / 2 \rfloor$ | 保留50%独立访问,平衡效率与风险 | -| 配对策略 | 每站点最多配对一次 | 简化实现,足以说明方法论 | -| 双站点访问计数 | 算1次访问事件 | 释放槽位给其他站点 | - -### D.2 有效性衰减公式(总量计算) - -双站点访问时,质量折扣因子按总服务量计算: - -$$q_{ij} = \min\left(1, \frac{250}{\mu_i + \mu_j}\right)$$ - -**E2'的计算**: -$$E_2' = \sum_{\text{单站点}} k_i \cdot q(\mu_i) \cdot \mu_i + \sum_{\text{双站点}} k_{ij} \cdot q(\mu_i + \mu_j) \cdot E[S_i + S_j]$$ - -### D.3 合并比例详述 - -对于配对 $(i, j)$,原频次为 $k_i, k_j$: - -| 项目 | 公式 | -|------|------| -| 双站点访问次数 | $k_{ij} = \lfloor \min(k_i, k_j) / 2 \rfloor$ | -| 站点i剩余单独访问 | $k_i' = k_i - k_{ij}$ | -| 站点j剩余单独访问 | $k_j' = k_j - k_{ij}$ | -| 释放的访问槽位 | $\Delta N = \sum k_{ij}$ | - ---- - -## 附录E:敏感性分析计划 - -### E.1 待分析参数 - -| 参数 | 基准值 | 扫描范围 | 预期影响 | -|------|--------|---------|---------| -| **合并比例** $r_{merge}$ | 1/2 | [1/3, 1/2, 2/3] | 配对数量、资源节省 | -| 距离阈值 $l_{max}$ | 50 mi | [30, 40, 50, 60, 70] | 可行配对数 | -| 容量上限 $\mu_i + \mu_j$ | 450 | [400, 425, 450, 475, 500] | 配对选择范围 | -| CV阈值 | 0.5 | [0.3, 0.4, 0.5, 0.6] | 配对稳定性 | - -### E.2 敏感性分析输出 - -- 各参数对 E1', E2', F1', R1 的影响曲线 -- 参数交互效应热力图 -- 稳健性结论 - ---- - -## 附录F:程序流水线 +## 附录B:程序流水线 ``` task3/ -├── 01_distance.py # 距离矩阵计算 -│ └── 01_distance.xlsx -├── 02_pairing.py # 配对筛选与选择 -│ └── 02_pairing.xlsx -├── 03_allocation.py # 最优分配计算 -│ └── 03_allocation.xlsx -├── 04_reschedule.py # 访问次数重分配 -│ └── 04_reschedule.xlsx -├── 05_calendar.py # 日历排程生成 -│ └── 05_calendar.xlsx -├── 06_evaluate.py # 效果评估 -│ └── 06_evaluate.xlsx -├── 07_sensitivity.py # 敏感性分析(待实现) -│ └── 07_sensitivity.xlsx -└── figures/ # 可视化输出 +├── 01_distance.py ✅ 距离矩阵计算 +│ └── 01_distance.xlsx (70×70矩阵) +├── 02_pairing.py ✅ 配对筛选与选择 +│ └── 02_pairing.xlsx (34对配对) +├── 03_allocation.py ✅ 最优分配计算 +│ └── 03_allocation.xlsx (q*值) +├── 04_reschedule.py ✅ 访问次数重分配 +│ └── 04_reschedule.xlsx (k'值) +├── 05_calendar.py ✅ 日历排程生成 +│ └── 05_calendar.xlsx (365天) +├── 06_evaluate.py ✅ 效果评估 +│ └── 06_evaluate.xlsx (指标对比) +├── 07_sensitivity.py ✅ 敏感性分析 +│ └── 07_sensitivity.xlsx (4参数) +├── 08_visualize.py ⏳ 可视化(待实现) +│ └── figures/ (5张图) +└── README.md ✅ 本文档 ``` + +--- + +## 附录C:运行命令 + +```bash +cd task3 + +# 完整流程 +python 01_distance.py +python 02_pairing.py +python 03_allocation.py +python 04_reschedule.py +python 05_calendar.py +python 06_evaluate.py +python 07_sensitivity.py + +# 一键运行(可选) +for i in 01 02 03 04 05 06 07; do python ${i}_*.py; done +``` + +--- + +## 附录D:参数设置汇总 + +| 参数 | 符号 | 基准值 | 敏感性 | 依据 | +|------|------|--------|--------|------| +| 卡车容量 | $Q$ | 400户 | - | 数据推断 | +| 距离阈值 | $l_{max}$ | 50 mi | 低 | 时间预算 | +| 容量上限 | $\mu_{sum,max}$ | 450 | 中(风险) | Q+10% | +| CV上限 | $CV_{max}$ | 0.5 | 低 | 经验值 | +| 合并比例 | $r_{merge}$ | 1/2 | **高** | 效率-风险平衡 | +| 鲁棒性水平 | $k$ | 1 | - | 84%保护 | +| 质量阈值 | $\bar{C}$ | 250 | - | Task 1定义 |