一种VRPTW初始解构造方法(含惩罚时间)

一种VRPTW初始解构造方法(含惩罚时间)

  在使用智能算法求解带时间窗的车辆路径规划问题(VRPTW)时,面临的第一个问题就是初始解的构造,不同与旅行商问题(TSP)只要将所有客户打乱顺序即可得到一个初始解。VRPTW的初始解构造需要考虑两点:

  1. 每辆车的载重量上限;
  2. 客户点的开始服务时间约束。

在使用智能算法(本文以邻域搜索为例)求解VRPTW问题时,想要构造一个可行的满足所有约束的初始解较为复杂,为了构造方便以及避免后续需要对所有产生的新解进行可行性判断,一般引入两个惩罚因子,一个是对重叠时间进行惩罚,一个是对超载进行惩罚。

一种构造初始解的方法

构造过程如下

流程图示意图

下面是python构造初始解的代码。

class CustomerType:

    def __init__(self, c_id, x, y, demand, begin, end, service):
        """

        :param c_id: 客户点id
        :param x: x轴坐标
        :param y: y轴坐标
        :param demand: 需求量
        :param begin: 早开始时间
        :param end: 晚开始时间
        :param service: 服务时长
        """
        self.c_id = c_id
        self.x = x
        self.y = y
        self.demand = demand
        self.begin = begin
        self.end = end
        self.service = service
        self.r = 0


class RouteType:
    def __init__(self):
        self.load = 0  # 单条路径载重
        self.sub_t = 0  # 单条路径违反各节点时间窗约束时长总和
        self.dis = 0  # 单挑路径总长度
        self.v = []  # 单条路径顾客节点序列


# 计算距离
def distance(c1: CustomerType, c2: CustomerType):
    return int(((c1.x - c2.x) ** 2 + (c1.y - c2.y) ** 2) ** 0.5)


# 读取数据
def read_in():
    f = open('solomon-instances/c101.txt', 'r')
    lines = f.readlines()
    count = 0
    for line in lines:
        count += 1
        if 10 <= count <= 10 + customerNumber:
            str_ = line.strip().split()
            str_ = [int(s) for s in str_]
            node = CustomerType(*str_)
            customers.append(node)
    for i in range(vehicleNumber):
        # 给每一条路径赋初始起点与虚拟终点
        routes.append(RouteType())
        routes[i].v.append(copy.deepcopy(customers[0]))
        routes[i].v.append(copy.deepcopy(customers[0]))
        routes[i].v[0].end = routes[i].v[0].begin
        routes[i].v[1].begin = routes[i].v[1].end
        # 算例中给出节点0有起始时间0和终止时间,所以如上赋值。
        routes[i].load = 0

    # 计算两点间的距离
    for i in range(customerNumber + 1):
        temp = []
        for j in range(customerNumber + 1):
            temp.append(distance(customers[i], customers[j]))
        graph.append(temp)


# 构造初始解
def construction():
    customers_set = [i for i in range(1, customerNumber + 1)]
    rd.shuffle(customers_set)

    current_route = 0
    # 以满足容量约束为目的的随机初始化
    # 即随机挑选一个节点插入到第m条路径中,若超过容量约束,则插入第m+1条路径
    # 且插入路径的位置由该路径上已存在的各节点的最早时间决定
    for c in customers_set:
        # 随机提取出一个节点,类似产生乱序随机序列的代码
        # 不满足容量约束,下一条车辆路线
        if routes[current_route].load + customers[c].demand > capacity:
            current_route += 1

        # 对路径中每一个节点查找,看是否能插入新节点
        for i in range(len(routes[current_route].v) - 1):
            if routes[current_route].v[i].begin <= customers[c].begin <= routes[current_route].v[i + 1].begin:
                routes[current_route].v.insert(i + 1, copy.deepcopy(customers[c]))
                # 判断时间窗开始部分是否满足,则加入该节点
                # 更新路径容量,节点类
                routes[current_route].load += customers[c].demand
                customers[c].r = current_route
                break

    # 初始化计算超过时间窗约束的总量
    for i in range(vehicleNumber):
        routes[i].sub_t = 0
        for j in range(1, len(routes[i].v)):
            routes[i].dis += graph[routes[i].v[j - 1].c_id][routes[i].v[j].c_id]
        # 更新超过时间窗总量
        update_sub_t(routes[i])


# 更新路径r对时间窗的违反量
def update_sub_t(r: RouteType):
    at = 0
    # 对每个节点分别计算超出时间窗的部分
    for j in range(1, len(r.v)):
        at = at + r.v[j - 1].service + graph[r.v[j - 1].c_id][r.v[j].c_id]
        if at > r.v[j].end:
            # 超过,记录
            r.sub_t += at - r.v[j].end
        else:
            # 未到达,等待
            at = r.v[j].begin


# 参数
customerNumber = 25
vehicleNumber = 5
capacity = 200

graph = []  # 记录距离
# 存储客户数据、当前解路线数据
customers = []
routes = []
if __name__ == '__main__':
    # 读取数据
    read_in()
    # 构造初始解
    construction()

参考资料:干货 |【算法】禁忌搜索算法(Tabu Search,TS)超详细通俗解析附C++代码实例

数据来源

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值