引言
遗传算法(GA)是模拟自然选择过程的搜索启发式算法,经常用于解决优化和搜索问题。旅行商问题(TSP)和车辆路径问题(VRP)是组合优化领域中的两个经典问题。在本文中,我们将探讨如何使用遗传算法来解决这两个问题,并提供 Python 代码实现。
1. 遗传算法简介
遗传算法受到生物进化的启发,其中最适应的个体被选择用于繁殖下一代。该算法的基本步骤如下:
- 初始化:产生一个随机的个体群体。
- 选择:根据适应度函数选择最适应的个体进行繁殖。
- 交叉:通过组合父母的基因来产生后代。
- 变异:以小概率随机地改变后代的基因。
- 替代:用新生成的后代替换当前种群中的个体。
- 终止:当达到某个终止条件(例如,迭代次数或适应度阈值)时停止。
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 代码实现。
具体过程请下载完整项目。