Files
mcm-mfp/task3/03_allocation.py
2026-01-19 11:57:19 +08:00

221 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Task 3 - Step 3: 第一站点最优分配计算
=====================================
输入: 02_pairing.xlsx (选中的配对列表)
输出: 03_allocation.xlsx (含最优分配量q*的配对列表)
最优分配公式:
q* = (σ_j * μ_i + σ_i * (Q - μ_j)) / (σ_i + σ_j)
鲁棒性约束:
q_final = clip(q*, μ_i - σ_i, Q - μ_j + σ_j)
物理意义:
- 波动大的站点需要更多"缓冲"
- 当σ_j >> σ_i时q* → μ_i精确分配给站点i
- 当σ_i >> σ_j时q* → Q - μ_j为站点j预留更多
"""
import pandas as pd
import numpy as np
from scipy import stats
# ============================================
# 参数设置
# ============================================
INPUT_FILE = '02_pairing.xlsx'
OUTPUT_FILE = '03_allocation.xlsx'
Q = 400 # 卡车容量
K_ROBUST = 1 # 鲁棒性水平 (84%保护)
# ============================================
# 读取数据
# ============================================
print("=" * 60)
print("Task 3 - Step 3: 第一站点最优分配计算")
print("=" * 60)
# 读取选中的配对
df_pairs = pd.read_excel(INPUT_FILE, sheet_name='selected_pairs')
print(f"\n读取配对数据: {len(df_pairs)}")
# ============================================
# 计算最优分配
# ============================================
print(f"\n" + "-" * 40)
print("计算最优分配 q*")
print("-" * 40)
def optimal_allocation(mu_i, sigma_i, mu_j, sigma_j, Q=400):
"""
计算双站点访问时第一站点的最优分配量
公式: q* = (σ_j * μ_i + σ_i * (Q - μ_j)) / (σ_i + σ_j)
"""
if sigma_i + sigma_j == 0:
# 边界情况:两站点都无波动
return mu_i
q_star = (sigma_j * mu_i + sigma_i * (Q - mu_j)) / (sigma_i + sigma_j)
return q_star
def robust_allocation(q_star, mu_i, sigma_i, mu_j, sigma_j, Q=400, k=1):
"""
应用鲁棒性约束
约束: μ_i - k*σ_i ≤ q ≤ Q - μ_j + k*σ_j
"""
q_lower = max(0, mu_i - k * sigma_i)
q_upper = min(Q, Q - mu_j + k * sigma_j)
# 确保上下界合理
if q_lower > q_upper:
# 如果约束冲突,取中点
q_final = (q_lower + q_upper) / 2
else:
q_final = np.clip(q_star, q_lower, q_upper)
return q_final, q_lower, q_upper
def expected_service(q, mu, sigma):
"""
计算截断期望 E[min(D, q)]
E[min(D, q)] = μ * Φ(z) - σ * φ(z) + q * (1 - Φ(z))
where z = (q - μ) / σ
当q > μ时大部分时候D < q期望接近μ
当q < μ时经常D > q期望接近q但略低
"""
if sigma == 0:
return min(mu, q)
z = (q - mu) / sigma
phi_z = stats.norm.pdf(z)
Phi_z = stats.norm.cdf(z)
return mu * Phi_z - sigma * phi_z + q * (1 - Phi_z)
# 计算每个配对的分配
results = []
for idx, row in df_pairs.iterrows():
mu_i = row['mu_i']
sigma_i = row['sigma_i']
mu_j = row['mu_j']
sigma_j = row['sigma_j']
# 计算最优分配
q_star = optimal_allocation(mu_i, sigma_i, mu_j, sigma_j, Q)
# 应用鲁棒性约束
q_final, q_lower, q_upper = robust_allocation(
q_star, mu_i, sigma_i, mu_j, sigma_j, Q, K_ROBUST
)
# 计算期望服务量
E_Si = expected_service(q_final, mu_i, sigma_i)
E_Sj = expected_service(Q - q_final, mu_j, sigma_j)
E_total = E_Si + E_Sj
# 计算分配比例
alloc_ratio = q_final / Q if Q > 0 else 0
results.append({
**row.to_dict(),
'q_star': q_star,
'q_lower': q_lower,
'q_upper': q_upper,
'q_final': q_final,
'q_ratio': alloc_ratio,
'E_Si': E_Si,
'E_Sj': E_Sj,
'E_total': E_total,
'efficiency': E_total / (mu_i + mu_j) if (mu_i + mu_j) > 0 else 0
})
df_allocation = pd.DataFrame(results)
# ============================================
# 统计信息
# ============================================
print(f"\n分配统计:")
print(f" - q* 范围: [{df_allocation['q_star'].min():.1f}, {df_allocation['q_star'].max():.1f}]")
print(f" - q_final 范围: [{df_allocation['q_final'].min():.1f}, {df_allocation['q_final'].max():.1f}]")
print(f" - 分配比例范围: [{df_allocation['q_ratio'].min():.2%}, {df_allocation['q_ratio'].max():.2%}]")
print(f" - 平均分配比例: {df_allocation['q_ratio'].mean():.2%}")
print(f"\n期望服务量统计:")
print(f" - E[S_i] 范围: [{df_allocation['E_Si'].min():.1f}, {df_allocation['E_Si'].max():.1f}]")
print(f" - E[S_j] 范围: [{df_allocation['E_Sj'].min():.1f}, {df_allocation['E_Sj'].max():.1f}]")
print(f" - E[total] 范围: [{df_allocation['E_total'].min():.1f}, {df_allocation['E_total'].max():.1f}]")
print(f" - 平均效率: {df_allocation['efficiency'].mean():.2%}")
# 检查是否有被约束裁剪的情况
clipped_lower = (df_allocation['q_final'] <= df_allocation['q_lower'] + 0.01).sum()
clipped_upper = (df_allocation['q_final'] >= df_allocation['q_upper'] - 0.01).sum()
print(f"\n鲁棒性约束影响:")
print(f" - 触及下界: {clipped_lower}")
print(f" - 触及上界: {clipped_upper}")
print(f" - 未被裁剪: {len(df_allocation) - clipped_lower - clipped_upper}")
# ============================================
# 显示关键配对的分配
# ============================================
print(f"\n" + "-" * 40)
print("高价值配对的分配方案 (Top 10)")
print("-" * 40)
display_cols = ['site_i_name', 'site_j_name', 'mu_i', 'mu_j', 'q_final', 'E_Si', 'E_Sj', 'E_total']
df_top = df_allocation.nlargest(10, 'value')[display_cols].copy()
df_top['site_i_name'] = df_top['site_i_name'].str[:25]
df_top['site_j_name'] = df_top['site_j_name'].str[:25]
print(df_top.to_string(index=False))
# ============================================
# 保存结果
# ============================================
with pd.ExcelWriter(OUTPUT_FILE, engine='openpyxl') as writer:
# Sheet 1: 完整分配结果
df_allocation.to_excel(writer, sheet_name='allocation', index=False)
# Sheet 2: 简化视图
simple_cols = ['site_i_id', 'site_j_id', 'site_i_name', 'site_j_name',
'mu_i', 'mu_j', 'sigma_i', 'sigma_j', 'k_i', 'k_j',
'q_final', 'E_Si', 'E_Sj', 'E_total', 'value']
df_allocation[simple_cols].to_excel(writer, sheet_name='allocation_simple', index=False)
# Sheet 3: 参数记录
params = pd.DataFrame({
'parameter': ['Q', 'K_ROBUST'],
'value': [Q, K_ROBUST],
'description': ['卡车容量', '鲁棒性水平(k*σ)']
})
params.to_excel(writer, sheet_name='parameters', index=False)
# Sheet 4: 汇总统计
summary = pd.DataFrame({
'metric': ['total_pairs', 'avg_q_ratio', 'avg_E_total', 'avg_efficiency',
'min_E_total', 'max_E_total', 'clipped_lower', 'clipped_upper'],
'value': [len(df_allocation),
df_allocation['q_ratio'].mean(),
df_allocation['E_total'].mean(),
df_allocation['efficiency'].mean(),
df_allocation['E_total'].min(),
df_allocation['E_total'].max(),
clipped_lower, clipped_upper]
})
summary.to_excel(writer, sheet_name='summary', index=False)
print(f"\n结果已保存至: {OUTPUT_FILE}")
print(" - Sheet 'allocation': 完整分配结果")
print(" - Sheet 'allocation_simple': 简化视图")
print(" - Sheet 'parameters': 参数记录")
print(" - Sheet 'summary': 汇总统计")
print("\n" + "=" * 60)