import csv
import random
import math
from typing import List, Tuple
class City:
def __init__(self, name: str, longitude: float, latitude: float):
self.name = name
self.longitude = longitude
self.latitude = latitude
def __repr__(self):
return self.name
def euclidean_distance(city1: City, city2: City) -> float:
x_diff = city1.longitude - city2.longitude
y_diff = city1.latitude - city2.latitude
return math.sqrt(x_diff ** 2 + y_diff ** 2)
def load_cities_from_csv(file_path: str) -> List[City]:
cities = []
with open(file_path, "r", encoding="utf-8") as file:
csv_reader = csv.reader(file, delimiter=";")
for row in csv_reader:
city = City(row[0], float(row[1]), float(row[2]))
cities.append(city)
return cities
def path_distance(path: List[City]) -> float:
distance = sum(euclidean_distance(path[i - 1], path[i]) for i in range(1, len(path)))
distance += euclidean_distance(path[-1], path[0]) # 最后一个城市返回到第一个城市
return distance
class GeneticAlgorithm:
def __init__(
self,
cities: List[City],
population_size: int = 100,
generations: int = 1000,
mutation_rate: float = 0.01,
elite_size: int = 10
):
self.cities = cities
self.population_size = population_size
self.generations = generations
self.mutation_rate = mutation_rate
self.elite_size = elite_size
def create_population(self) -> List[List[City]]:
return [random.sample(self.cities, len(self.cities)) for _ in range(self.population_size)]
def fitness(self, path: List[City]) -> float:
return 1 / path_distance(path)
def selection(self, population: List[List[City]]) -> List[List[City]]:
ranked_population = sorted(population, key=self.fitness, reverse=True)
selected = ranked_population[:self.elite_size]
weights = [self.fitness(path) for path in ranked_population]
selected.extend(random.choices(ranked_population, weights, k=self.population_size - self.elite_size))
return selected
def crossover(self, parent1: List[City], parent2: List[City]) -> List[City]:
start, end = random.sample(range(len(parent1)), 2)
start, end = min(start, end), max(start, end)
child = [None] * len(parent1)
child[start:end] = parent1[start:end]
for city in parent2[end:] + parent2[:end]:
if city not in child:
index = child.index(None)
child[index] = city
return child
def mutate(self, path: List[City]) -> List[City]:
for i in range(len(path)):
if random.random() < self.mutation_rate:
j = random.randint(0, len(path) - 1)
path[i], path[j] = path[j], path[i]
return path
def evolve(self, population: List[List[City]]) -> List[List[City]]:
selected = self.selection(population)
children = []
for i in range(0, len(selected) - 1, 2):
child1 = self.crossover(selected[i], selected[i + 1])
child2 = self.crossover(selected[i + 1], selected[i])
children.extend([child1, child2])
if len(children) < len(population):
children.append(selected[-1])
return [self.mutate(child) for child in children]
def run(self) -> Tuple[List[City], float]:
population = self.create_population()
for _ in range(self.generations):
population = self.evolve(population)
best_path = max(population, key=self.fitness)
return best_path, path_distance(best_path)
# 读取城市数据
cities = load_cities_from_csv("china.csv")
# 运行遗传算法
ga = GeneticAlgorithm(cities, population_size=100, generations=1000, mutation_rate=0.01, elite_size=10)
best_path, distance = ga.run()
#输出结果
print("最短路径为:")
for city in best_path:
print(f"{city.name} ({city.longitude}, {city.latitude})")
print(f"总距离:{distance}")