单选址问题
比较了1)迭代法,2)重心法和3)one-median法的差异。总体而言从加权距离最小的角度看,迭代法选出来的结果最好。当网点和网点的单量呈现偏态分布时,one-median的结果更好,当网点和网点的单量呈现呈现随机分布时,重心法和one-median没有明显的差异。
参考链接:https://wiki.mbalib.com/zh-tw/%E9%87%8D%E5%BF%83%E6%B3%95
代码
import numpy as np
import pandas as pd
import random
import math as m
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
plt.rcParams['font.sans-serif'] = [u'SimHei']
"""
单选址问题
比较了1)迭代法,2)重心法和3)one-median法的差异。总体而言从加权距离最小的角度看,迭代法选出来的结果最好。当网点和网点的单量呈现偏态分布时,one-median的结果更好,当网点和网点的单量呈现呈现随机分布时,重心法和one-median没有明显的差异。
参考链接:https://wiki.mbalib.com/zh-tw/%E9%87%8D%E5%BF%83%E6%B3%95
"""
class Weight:
def __init__(self):
pass
def generate_data_handle(self):
# 生成数据
x_list = [6, 4, 1, 9, 3]
y_list = [7, 2, 6, 3, 10]
w_list = [8000, 7000, 5000, 4000, 6000]
cost_list = [1, 1, 1, 1, 1]
data = pd.DataFrame({'x': x_list, 'y':y_list, 'w': w_list, 'cost': cost_list})
data['w'] = data['w'] * data['cost']
data['w^2'] = data['w'] * data['w']
self.data = data
def generate_data_random(self, batch_size=20, p_lb=0, p_ub=100):
# 生成数据
y_list = [random.randint(60, 110) for i in range(batch_size)]
x_list = [random.randint(20, 50) for i in range(batch_size)]
w_list = [random.randint(10, 100)*x_list[i] for i in range(batch_size)]
cost_list = [1 for i in range(batch_size)]
data = pd.DataFrame({'x': x_list, 'y': y_list, 'w': w_list, 'cost': cost_list})
data['w'] = data['w'] * data['cost']
data['w^2'] = data['w'] * data['w']
self.data = data
def generate_data_skewness(self, batch_size=20, p_lb=0, p_ub=100):
# 生成数据
y_list = [random.randint(60, 110) for i in range(2*batch_size)]
y_list.extend([random.randint(80, 110) for i in range(2*batch_size)])
x_list = [random.randint(20, 50) for i in range(2*batch_size)]
x_list.extend([random.randint(40, 50) for i in range(2*batch_size)])
w_list = [random.randint(10, 100)*x_list[i] for i in range(4*batch_size)]
cost_list = [1 for i in range(4*batch_size)]
data = pd.DataFrame({'x': x_list, 'y': y_list, 'w': w_list, 'cost': cost_list})
data['w'] = data['w'] * data['cost']
data['w^2'] = data['w'] * data['w']
self.data = data
def compute_node_weight(self, data, weight_col_name, d_j):
# 普通重心法:加权均值:(x0, y0)
WC = np.array(data[weight_col_name])
WC_j = WC / d_j
WCX = ((np.array(data['x']) * WC)/d_j).sum()
WCY = ((np.array(data['y']) * WC)/d_j).sum()
x = WCX / WC_j.sum()
y = WCY / WC_j.sum()
d_j = ((np.array(data['x']) - x) ** 2 + (np.array(data['y']) - y) ** 2) ** 0.5
transportation_cost = (np.array(data['w']) * d_j).sum()
return x, y, d_j, transportation_cost
def run(self, iter_num=10):
# one - median法: 加权平方均值
x2, y2, d_j, transportation_cost2 = self.compute_node_weight(self.data, 'w^2', 1)
print('加权平方重心选点大致位置:({},{}), 总运输费用:{}'.format(x2, y2, transportation_cost2))
# 加权均值
x0, y0, d_j, transportation_cost0 = self.compute_node_weight(self.data, 'w', 1)
print('重心法选点大致位置:({},{}), 总运输费用:{}'.format(x0, y0, transportation_cost0))
self.p0 = [x0, y0, transportation_cost0]
self.p2 = [x2, y2, transportation_cost2]
min_transportation_cost = transportation_cost0
result_list = [[x0, y0, transportation_cost0, 0]]
for i in range(iter_num):
x, y, d_j, transportation_cost = self.compute_node_weight(self.data, 'w', d_j)
result_list.append([x, y, transportation_cost, i+1])
print('经{}次迭代后选址点位置:({},{}), 总运输费用:{}'.format(i + 1, x, y, transportation_cost))
if min_transportation_cost - transportation_cost < 1:
break
min_transportation_cost = transportation_cost
self.result = result_list
def plot_figure(self):
# 初始化函数用于绘制一块干净的画布,为后续绘图做准备
result = self.result
p0, p2 = self.p0, self.p2
scatter_size = 200 # [200, 200, 200, 200, 200, 200]
# 生成画布
# plt.figure(figsize=(6, 6), dpi=80)
plt.subplot(1, 2, 1)
# 打开交互模式
plt.ion()
for step in range(len(result)):
# 清除原有图像
plt.cla()
position = result[step]
plt.scatter(self.data['x'], self.data['y'], scatter_size, c='pink', marker='*', alpha=0.7, label='配送点')
plt.scatter(p0[0], p0[1], scatter_size, c='blue', marker='p', alpha=0.7, label='加权重心')
plt.scatter(p2[0], p2[1], scatter_size, c='green', marker='p', alpha=0.7, label='加权平方重心')
plt.scatter(position[0], position[1], scatter_size, c='red', marker='p', alpha=0.7, label='选址点')
plt.xlabel('纬度', fontsize=11)
plt.ylabel('经度', fontsize=11)
plt.grid(True)
# plt.text(1,3,'总费用{}'.format(T),fontsize=16)
plt.title('重心法选址,第{}次结果示意图'.format(step + 1), fontsize=14)
# plt.title('重心法选址结果示意图', fontsize=14)
plt.legend(loc='lower left')
plt.pause(0.2)
# 关闭交互模式
plt.ioff()
# 图形显示
# plt.show()
def plot_iteration(self):
result = self.result
p0, p2 = self.p0, self.p2
iter_num = len(result)
# 生成画布
# plt.figure(figsize=(6, 6), dpi=80)
plt.subplot(1, 2, 2)
plt.plot([0, iter_num-1], [p0[2], p0[2]], c='blue', label='加权重心')
plt.plot([0, iter_num-1], [p2[2], p2[2]], c='green', label='加权平方重心')
plt.plot([i for i in range(iter_num)], [p[2] for p in result], c='red', label='重心法')
plt.xlabel('迭代次数', fontsize=11)
plt.ylabel('总运输成本', fontsize=11)
plt.grid(True)
plt.title('重心法选址结果示意图', fontsize=14)
plt.legend(loc='lower left')
plt.show()
def plot_figure_subplot(self):
# 初始化函数用于绘制一块干净的画布,为后续绘图做准备
result = self.result
p0, p2 = self.p0, self.p2
scatter_size = 200 # [200, 200, 200, 200, 200, 200]
iter_num = len(result)
# 生成画布
plt.figure(figsize=(10, 6), dpi=80)
plt.subplot(1, 2, 1)
# 打开交互模式
plt.ion()
for step in range(len(result)):
# 清除原有图像
plt.cla()
position = result[step]
plt.scatter(self.data['x'], self.data['y'], scatter_size, c='pink', marker='*', alpha=0.7, label='配送点')
plt.scatter(p0[0], p0[1], scatter_size, c='blue', marker='p', alpha=0.7, label='重心法')
plt.scatter(p2[0], p2[1], scatter_size, c='green', marker='p', alpha=0.7, label='one-median')
plt.scatter(position[0], position[1], scatter_size, c='red', marker='p', alpha=0.7, label='迭代法选址点')
plt.xlabel('纬度', fontsize=11)
plt.ylabel('经度', fontsize=11)
plt.grid(True)
# plt.text(1,3,'总费用{}'.format(T),fontsize=16)
plt.title('重心法选址,第{}次结果示意图'.format(step + 1), fontsize=14)
# plt.title('重心法选址结果示意图', fontsize=14)
plt.legend(loc='lower left')
plt.pause(0.2)
# 关闭交互模式
plt.ioff()
plt.subplot(1, 2, 2)
plt.plot([0, iter_num-1], [p0[2], p0[2]], c='blue', label='重心法')
plt.plot([0, iter_num-1], [p2[2], p2[2]], c='green', label='one-median')
plt.plot([i for i in range(iter_num)], [p[2] for p in result], c='red', label='迭代法选址点')
plt.xlabel('迭代次数', fontsize=11)
plt.ylabel('总运输成本', fontsize=11)
plt.grid(True)
plt.title('重心法选址结果示意图', fontsize=14)
plt.legend(loc='lower left')
plt.show()
if __name__ == "__main__":
weight_compare = Weight()
# 生成小测试算例
# weight_compare.generate_data_handle()
# 生成随机数
# weight_compare.generate_data_random(batch_size=30, p_lb=0, p_ub=100)
# 生成偏态分布
weight_compare.generate_data_skewness(batch_size=20)
weight_compare.run(iter_num=50)
# weight_compare.plot_iteration()
# weight_compare.plot_figure()
weight_compare.plot_figure_subplot()
结果展示
数据为随机分布的结果 数据为偏态分布的结果
注:应该重负做100次试验,比较成本的统计结果