选址问题
选址问题是运筹学中经典的问题之一。选址问题在生产生活、物流、甚至军事中都有着非常广泛的应用,如工厂、仓库、急救中心、消防站、垃圾处理中心、物流中心、导弹仓库的选址等。
基本问题:
- P-中位问题(p-median problems):
P-中位问题(也叫P-中值问题)是研究如何选择P个服务站使得需求点和服务站之间的距离与需求量的乘积之和最小。 - P-中心问题(p-center problems):
P-中心问题也叫 minmax 问题,是探讨如何在网络中选择 P 个服务站,使得任意一需求点到距离该需求点最近的服务站的最大距离最小问题。 - 覆盖问题(covering problems):
覆盖问题分为最大覆盖问题和集覆盖问题两类。集覆盖问题研究满足覆盖所有需求点顾客的前提下,服务站总的建站个数或建设费用最小的问题。
在前面三个基本选址问题的基础上考虑其它因素就形成了扩展选址问题。我们以带固定费用和容量限制的选址问题为例并用爬山算法和模拟退火算法进行求解。
带固定费用和容量限制的选址问题
给定若干个带容量限制的服务站,并给每个服务站定义一个固定费用,只要该有任何需求点分配给该服务站则需要消耗该固定费用(相当于开启该服务站的费用)。另外每个需求点分配给服务站时也有一定的费用。总费用=固定费用+分配费用。
现在要求给出一个分配方案,将若干个服务站的其中一部分或全部分配给相应所有的需求点,确保所有需求点都被分配到,并且让总费用最小。
算法实现
算法用python实现,完整代码在github上。
当前解表示
首先需要选择合适的方式来表示当前解,方便后续算法的实现。这里我们定义了服务站和需求点两个类。
class Customer:
'需求点'
def __init__(self, cid, demand):
self.id = cid
self.demand = demand
self.fid = -1
self.assignment_cost = -1
def __str__(self):
return 'Customer(id:%d, demand:%d, fid:%d, cost:%d)' % (self.id, self.demand, self.fid, self.assignment_cost)
class Ficility:
'服务站'
def __init__(self, fid, capacity, opening_cost, assignment_costs):
self.id = fid
self.capacity = capacity
self.opening_cost = opening_cost
self.assignment_costs = assignment_costs
self.assignment = []
self.cur_capacity = capacity
self.total_cost = 0
# 计算将某一需求点分配到该服务站所新增的代价
def get_cost(self, customer):
if self.cur_capacity < customer.demand:
return float("inf")
if len(self.assignment) == 0:
return self.opening_cost + self.assignment_costs[customer.id]
return self.assignment_costs[customer.id]
# 将某一需求点分配到该服务站并更新服务站和需求点的状态,返回所需的cost
def assign(self, customer):
cost = self.get_cost(customer)
if cost == float('inf'):
return 0
self.total_cost += cost
self.cur_capacity -= customer.demand
self.assignment.append(customer)
customer.fid = self.id
customer.assignment_cost = self.assignment_costs[customer.id]
return cost
# 将某一需求点从该服务站的分配列表中删除并更新服务站和需求点的状态,返回减少的cost
def deassign(self, customer