山区医疗点选址优化:最小化村民看病距离与道路维修成本

假设某山区中有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 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录