P3: main
This commit is contained in:
220
task3/03_allocation.py
Normal file
220
task3/03_allocation.py
Normal file
@@ -0,0 +1,220 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user