使用遗传算法优化旅行商问题 (TSP) 和车辆路径问题 (VRP) 的深入 Python 实现

引言

遗传算法(GA)是模拟自然选择过程的搜索启发式算法,经常用于解决优化和搜索问题。旅行商问题(TSP)和车辆路径问题(VRP)是组合优化领域中的两个经典问题。在本文中,我们将探讨如何使用遗传算法来解决这两个问题,并提供 Python 代码实现。

1. 遗传算法简介

遗传算法受到生物进化的启发,其中最适应的个体被选择用于繁殖下一代。该算法的基本步骤如下:

  1. 初始化:产生一个随机的个体群体。
  2. 选择:根据适应度函数选择最适应的个体进行繁殖。
  3. 交叉:通过组合父母的基因来产生后代。
  4. 变异:以小概率随机地改变后代的基因。
  5. 替代:用新生成的后代替换当前种群中的个体。
  6. 终止:当达到某个终止条件(例如,迭代次数或适应度阈值)时停止。

2. 旅行商问题 (TSP)

旅行商问题是组合优化中的一个经典问题,描述的是一个旅行商需要访问所有的城市并返回到原始城市,同时尽量减少总的旅行距离。

问题定义:

给定一组城市和每两个城市之间的距离,找到访问每个城市一次并返回到出发城市的最短路径。

Python 代码实现:

首先,我们需要定义城市、适应度函数和初始化种群的函数。

import numpy as np

class City:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def distance(self, city):
        xDis = abs(self.x - city.x)
        yDis = abs(self.y - city.y)
        distance = np.sqrt((xDis ** 2) + (yDis ** 2))
        return distance

def fitness(route):
    pathDistance = 0
    for i in range(0, len(route) - 1):
        pathDistance += route[i].distance(route[i + 1])
    return 1 / float(pathDistance)

def initialize_population(popSize, cityList):
    population = []

    for i in range(0, popSize):
        population.append(np.random.permutation(cityList))
    return population

上面的代码定义了一个 City 类,它有一个计算到另一个城市的距离的方法。fitness 函数计算了给定路线的适应度值,而 initialize_population 函数则用于初始化种群。

3. 遗传算法操作

要使用遗传算法解决 TSP,我们需要实现选择、交叉、变异和替代操作。

选择操作:

选择操作的目标是从当前种群中选择两个“父母”进行交叉和变异。通常,我们使用轮盘赌选择法。

def roulette_wheel_selection(population):
    # Calculate the fitness of each route in the population
    fitness_values = [fitness(route) for route in population]

    # Normalize the fitness values
    total_fitness = sum(fitness_values)
    normalized_fitness_values = [f / total_fitness for f in fitness_values]

    # Choose the first parent
    r1 = np.random.rand()
    accumulated_probability = 0.0
    for i, normalized_fitness in enumerate(normalized_fitness_values):
        accumulated_probability += normalized_fitness
        if r1 < accumulated_probability:
            parent1 = population[i]
            break

    # Choose the second parent (distinct from the first)
    r2 = np.random.rand()
    accumulated_probability = 0.0
    for i, normalized_fitness in enumerate(normalized_fitness_values):
        accumulated_probability += normalized_fitness
        if r2 < accumulated_probability and population[i] != parent1:
            parent2 = population[i]
            break

    return parent1, parent2

交叉操作:

交叉操作的目标是产生一个新的“孩子”路线,继承了父母的特性。我们使用顺序交叉。

def ordered_crossover(parent1, parent2):
    child = [None] * len(parent1)

    # Randomly select a subset from the first parent
    start_pos = np.random.randint(len(parent1))
    end_pos = np.random.randint(start_pos, len(parent1))

    for i in range(start_pos, end_pos):
        child[i] = parent1[i]

    # Fill the remaining positions with the genes from the second parent
    position = end_pos
    for city in parent2:
        if city not in child:
            if position == len(child):
                position = 0
            child[position] = city
            position += 1

    return child

变异操作:

变异操作的目标是在后代中引入遗传多样性。我们使用交换变异。

def swap_mutation(route, mutation_rate):
    new_route = route.copy()
    for i in range(len(route)):
        if np.random.rand() < mutation_rate:
            swap_with = np.random.randint(len(route))
            city1 = new_route[i]
            city2 = new_route[swap_with]
            new_route[i] = city2
            new_route[swap_with] = city1
    return new_route

替代操作:

替代操作的目标是更新种群。我们简单地用新的后代替换了当前种群中适应度最差的个体。

def replace_worst_population(population, children):
    combined = population + children
    sorted_population = sorted(combined, key=fitness, reverse=True)
    return sorted_population[:len(population)]

这部分代码实现了 GA 的核心操作,这些操作将在 GA 的主循环中被调用。

4. 遗传算法的主循环

现在我们已经定义了所有必要的操作,我们可以实现遗传算法的主循环来解决 TSP。

def genetic_algorithm_TSP(cityList, popSize, eliteSize, mutationRate, generations):
    population = initialize_population(popSize, cityList)
    progress = []

    # Initial distance (inverse of fitness)
    progress.append(1 / fitness(sorted(population, key=fitness, reverse=True)[0]))

    # Main loop
    for generation in range(generations):
        # Selection
        parent1, parent2 = roulette_wheel_selection(population)

        # Crossover
        child1 = ordered_crossover(parent1, parent2)
        child2 = ordered_crossover(parent2, parent1)

        # Mutation
        child1 = swap_mutation(child1, mutationRate)
        child2 = swap_mutation(child2, mutationRate)

        # Replacement
        population = replace_worst_population(population, [child1, child2])

        # Store progress
        progress.append(1 / fitness(sorted(population, key=fitness, reverse=True)[0]))

    bestRoute = sorted(population, key=fitness, reverse=True)[0]
    return bestRoute, progress

5. 车辆路径问题 (VRP)

车辆路径问题是 TSP 的一种扩展,其中有多辆车和一个集散中心,目标是为所有客户提供服务,同时最小化总的旅行成本和使用的车辆数量。

问题定义:

给定一组客户、每两个客户之间的距离、每个客户的需求、车辆的容量和数量,找到访问每个客户一次并返回到出发地的最低成本路线。

注意:由于 VRP 是一个复杂问题,我们只提供一个简化版本的解决方案。

Python 代码实现:

为了解决 VRP,我们可以修改 TSP 的适应度函数来考虑车辆容量和数量。我们还需要定义客户的需求。

class Customer(City):
    def __init__(self, x, y, demand):
        super().__init__(x, y)
        self.demand = demand

def vrp_fitness(route, vehicle_capacity):
    total_distance = 0
    total_demand = 0
    vehicle_count = 1

    for i in range(0, len(route) - 1):
        total_distance += route[i].distance(route[i + 1])
        total_demand += route[i].demand

        if total_demand > vehicle_capacity:
            total_demand = 0
            vehicle_count += 1

    return 1 / (total_distance + vehicle_count * 1000)  # We add a penalty for each vehicle used

我们假设所有车辆都从相同的集散中心出发,然后访问客户并返回集散中心。为了简化问题,我们没有考虑车辆的实际数量,但在适应度函数中为每使用一辆车加上了惩罚。

总结:

遗传算法是一种强大的工具,可用于解决组合优化问题,如 TSP 和 VRP。在本文中,我们已经展示了如何使用遗传算法的基本操作来解决这些问题,并为您提供了完整的 Python 代码实现。

具体过程请下载完整项目。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快撑死的鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值