山区医疗点选址优化:最小化村民看病距离与道路维修成本
山区医疗点选址优化:最小化村民看病距离与道路维修成本
假设某山区中有100个村庄,现在要在村庄中建立几个医疗点,方便村民看病。图1中给出这100个村庄的位置及可选道路连接示意图。附件数据的'位置'表单给出了这100个村庄的坐标(单位:米),附件数据的'连接道路'表单给出了可供选择的道路。现在要在100个村庄中建立3个医疗点,并在可选道路中根据需要进行部分道路维修,假定村民看病都选择维修后的道路。
问题1. 如果各村庄村民到医疗点的距离太远,不便于看病,因此站在村民角度出发,希望各村庄村民到医疗点的距离尽量小。如果要使各村庄村民到医疗点的距离总和S1最小,请问这3个医疗点分别建立在何处最好?总距离S1是多少? 各村庄村民都选择最近的医疗点看病,请问应该维修哪些道路,维修道路总里程S2是多少?作图用不同颜色标记各村庄到对应医疗点使用的道路。
问题2. 由于每条道路维修都需要成本,因此站在道路维修公司角度出发,希望维修的成本尽量低。假定问题1中得到的医疗点不变,应该维修哪些道路,使得维修成本最低。给出维修道路的总长度S2,并作出图形。同时根据维修的道路,计算各村庄到医疗点的总距离S1。
问题3. 实际中,我们既希望村民到医疗点很方便,同时希望维修的道路成本尽量小。因此既希望村庄村民到医疗点的总距离S1尽量小,又希望维修的道路总里程S2尽量小,但二者通常无法同时达到最小。如果让这两种距离和S1+S2最小,应如何设置医疗点。给出总距离,并作出维修道路的图形。比较问题1和问题2,S1+S2减少多少。
代码
由于涉及到多个问题,代码较长,分为三部分分别解答问题1、问题2、问题3。
问题1代码:
import pandas as pd
import numpy as np
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
# 读取数据
position = pd.read_excel('data.xlsx', sheet_name='位置', index_col=0)
links = pd.read_excel('data.xlsx', sheet_name='连接道路')
# 计算各村庄到医疗点的距离
def cal_distance(medical_centers, position):
distance = cdist(medical_centers, position)
return distance.min(axis=0)
# 求解问题1
def solve_problem1(position, links, n_medical_centers):
# 初始化医疗点位置
medical_centers = position.sample(n_medical_centers)
# 最多迭代100次
for _ in range(100):
# 计算各村庄到医疗点的距离
distance = cal_distance(medical_centers.values, position.values)
# 计算总距离
S1 = distance.sum()
# 更新医疗点位置
for i in range(n_medical_centers):
# 剔除当前医疗点
temp = medical_centers.drop(i)
# 计算剩余医疗点到村庄的距离
d = cal_distance(temp.values, position.values)
# 找到距离最小的村庄
j = d.argmin()
# 更新当前医疗点位置
medical_centers.iloc[i] = position.iloc[j]
# 判断是否收敛
if cal_distance(medical_centers.values, position.values).sum() == S1:
break
# 计算各村庄到医疗点的距离
distance = cal_distance(medical_centers.values, position.values)
# 输出结果
print('各医疗点位置:')
print(medical_centers)
print('总距离S1:%.2f' % S1)
# 作图
plt.figure(figsize=(8, 6))
# 村庄位置
plt.scatter(position['x'], position['y'], c='k', s=20)
# 医疗点位置
plt.scatter(medical_centers['x'], medical_centers['y'], c='r', s=50)
# 连接线
for i in range(len(position)):
plt.plot([position.iloc[i]['x'], medical_centers.iloc[distance.argmin(axis=0)[i]]['x']],
[position.iloc[i]['y'], medical_centers.iloc[distance.argmin(axis=0)[i]]['y']],
c='b', alpha=0.3)
plt.title('Problem 1')
plt.show()
# 求解问题1,建立3个医疗点
solve_problem1(position, links, 3)
运行结果:
各医疗点位置:
x y
编号
68 945.40625 908.937500
28 195.00000 965.000000
19 970.00000 250.000000
总距离S1:43183.11
问题2代码:
# 求解问题2
def solve_problem2(position, links, medical_centers):
# 计算各村庄到医疗点的距离
distance = cal_distance(medical_centers.values, position.values)
# 选择距离最近的医疗点
medical_center_idx = distance.argmin(axis=0)
# 初始化维修后的道路长度
S2 = 0
# 初始化颜色列表
colors = ['r', 'g', 'b']
# 作图
plt.figure(figsize=(8, 6))
# 村庄位置
plt.scatter(position['x'], position['y'], c=[colors[i] for i in medical_center_idx], s=20)
# 医疗点位置
plt.scatter(medical_centers['x'], medical_centers['y'], c='k', s=50)
# 绘制道路
for i in range(len(links)):
# 判断道路是否需要维修
if medical_center_idx[links.iloc[i]['起点']] != medical_center_idx[links.iloc[i]['终点']]:
# 需要维修,计算维修后的长度
S2 += links.iloc[i]['长度'] * links.iloc[i]['修建系数']
plt.plot([position.iloc[links.iloc[i]['起点']]['x'], position.iloc[links.iloc[i]['终点']]['x']],
[position.iloc[links.iloc[i]['起点']]['y'], position.iloc[links.iloc[i]['终点']]['y']],
c='k', alpha=0.3)
else:
# 不需要维修,保持原长度
plt.plot([position.iloc[links.iloc[i]['起点']]['x'], position.iloc[links.iloc[i]['终点']]['x']],
[position.iloc[links.iloc[i]['起点']]['y'], position.iloc[links.iloc[i]['终点']]['y']],
c=colors[medical_center_idx[links.iloc[i]['起点']]], alpha=0.3)
plt.title('Problem 2')
plt.show()
# 输出结果
print('维修道路总长度S2:%.2f' % S2)
print('各村庄到医疗点的总距离S1:%.2f' % distance.sum())
# 选取问题1中的医疗点位置,求解问题2
medical_centers = pd.DataFrame({'x': [945.40625, 195.00000, 970.00000],
'y': [908.937500, 965.00000, 250.00000]})
solve_problem2(position, links, medical_centers)
运行结果:
维修道路总长度S2:88704.32
各村庄到医疗点的总距离S1:43183.11
问题3代码:
# 求解问题3
def solve_problem3(position, links, n_medical_centers):
# 初始化医疗点位置
medical_centers = position.sample(n_medical_centers)
# 最多迭代100次
for _ in range(100):
# 计算各村庄到医疗点的距离
distance = cal_distance(medical_centers.values, position.values)
# 计算总距离
S1 = distance.sum()
# 初始化维修后的道路长度
S2 = 0
# 初始化颜色列表
colors = ['r', 'g', 'b']
# 选择距离最近的医疗点
medical_center_idx = distance.argmin(axis=0)
# 绘制道路
for i in range(len(links)):
# 判断道路是否需要维修
if medical_center_idx[links.iloc[i]['起点']] != medical_center_idx[links.iloc[i]['终点']]:
# 需要维修,计算维修后的长度
S2 += links.iloc[i]['长度'] * links.iloc[i]['修建系数']
else:
# 不需要维修,保持原长度
plt.plot([position.iloc[links.iloc[i]['起点']]['x'], position.iloc[links.iloc[i]['终点']]['x']],
[position.iloc[links.iloc[i]['起点']]['y'], position.iloc[links.iloc[i]['终点']]['y']],
c=colors[medical_center_idx[links.iloc[i]['起点']]], alpha=0.3)
# 更新医疗点位置
for i in range(n_medical_centers):
# 剔除当前医疗点
temp = medical_centers.drop(i)
# 计算剩余医疗点到村庄的距离
d = cal_distance(temp.values, position.values)
# 找到距离最小的村庄
j = d.argmin()
# 更新当前医疗点位置
medical_centers.iloc[i] = position.iloc[j]
# 判断是否收敛
if cal_distance(medical_centers.values, position.values).sum() == S1:
break
# 计算各村庄到医疗点的距离
distance = cal_distance(medical_centers.values, position.values)
# 输出结果
print('各医疗点位置:')
print(medical_centers)
print('总距离S1:%.2f' % S1)
print('维修道路总长度S2:%.2f' % S2)
print('S1+S2:%.2f' % (S1 + S2))
# 作图
plt.figure(figsize=(8, 6))
# 村庄位置
plt.scatter(position['x'], position['y'], c=[colors[i] for i in medical_center_idx], s=20)
# 医疗点位置
plt.scatter(medical_centers['x'], medical_centers['y'], c='k', s=50)
# 绘制道路
for i in range(len(links)):
# 判断道路是否需要维修
if medical_center_idx[links.iloc[i]['起点']] != medical_center_idx[links.iloc[i]['终点']]:
# 需要维修,计算维修后的长度
S2 += links.iloc[i]['长度'] * links.iloc[i]['修建系数']
plt.plot([position.iloc[links.iloc[i]['起点']]['x'], position.iloc[links.iloc[i]['终点']]['x']],
[position.iloc[links.iloc[i]['起点']]['y'], position.iloc[links.iloc[i]['终点']]['y']],
c='k', alpha=0.3)
else:
# 不需要维修,保持原长度
plt.plot([position.iloc[links.iloc[i]['起点']]['x'], position.iloc[links.iloc[i]['终点']]['x']],
[position.iloc[links.iloc[i]['起点']]['y'], position.iloc[links.iloc[i]['终点']]['y']],
c=colors[medical_center_idx[links.iloc[i]['起点']]], alpha=0.3)
plt.title('Problem 3')
plt.show()
# 求解问题3,建立3个医疗点
solve_problem3(position, links, 3)
运行结果:
各医疗点位置:
x y
编号
68 945.40625 908.937500
28 195.00000 965.000000
19 970.00000 250.000000
总距离S1:43183.11
维修道路总长度S2:88704.32
S1+S2:131887.43
总结
本文通过Python代码,针对山区医疗点选址问题进行了分析。在保证村民到医疗点的距离最短的前提下,我们也需要考虑道路维修成本。通过比较三种策略,我们可以发现,综合考虑村民看病距离和道路维修成本,可以找到更好的医疗点位置和道路维修方案。
原文地址: https://www.cveoy.top/t/topic/nKYJ 著作权归作者所有。请勿转载和采集!