遗传算法
操作过程
遗传算法由自然界的遗传机制得来,操作流程一般为: 初始化 -> 选择 -> 交叉 -> 变异 -> 迭代
下面以旅行商问题为示例:
初始化(编码 + 定义适应度函数)
- 编码:
将问题解用基因编码并随机生成一组候选解。
在旅行商问题中,可以将路线转化为整数编码,随机生成n条路线作为一个种群
eg:[0,1,2,3,4,5,6,7,8,9], [9,8,7,6,5,4,3,2,1,0], ..... - 定义适应度函数
定义适应度函数用来判断每个候选解的优劣,以便后续进行筛选。
旅行商问题中需要得到总距离最短的路线,所以可以使用距离的倒数作为适应度函数
$$
f = \frac{1}{D}
$$
此处可以定义城市之间的距离矩阵进行预处理,以便后续计算
选择
按照适应度高低选择优秀个体进入下一代,常用途径有轮盘赌、锦标赛选择等。
交叉
下面举双点交叉和顺序交叉两个例子
- 双点交叉
随机选取两个断点i < j,交换i到j中间的部分
取i = 3, j = 7
操作过程:
P1: [123 | 4567 | 890] -> [123 6543 890] -> [1236543890]
P2: [987 | 6543 | 210] -> [987 4567 210] -> [9874567210]
显然双点交叉会使不应该重复的元素重复出现,不适用旅行商问题,所以采用顺序交叉
- 顺序交叉
随机选取两个断点i < j,截取P1中i到j中间的部分,将P2从j + 1位置开始循环扫描,将未出现的城市依次填充到P1的空位
取i = 3, j = 7
P1 = [1 2 3 4 5 6 7 8 9 0] -> [_ _ 3 4 5 6 _ _ _ _] -> [2 1 3 4 5 6 0 9 8 7]
P2 = [9 8 7 6 5 4 3 2 1 0] -> [3 2 1 0 9 8 7 6 5 4] -> [2 1 0 9 8 7]
得到新序列P1,对P2同理可得
最后,将新生成的编码加入种群中。
此外,还可以使用部分匹配交叉、循环交叉等方法进行交叉。
变异
变异操作中,发生随机选取两个位置进行交换的小概率事件。
如旅行商问题中,让每个路径有1%的概率发生变异:
eg: [1 2 3 4 5 6 7 8 9 0] -> [1 9 3 4 5 6 7 8 2 0]
最后,将新生成的编码加入种群中。
迭代
可以设置最大迭代次数或目标适应度以结束迭代。
也可以设置连续n此迭代后最优路线不改变则结束迭代。
没有达到结束条件则对新得到的种群重新进行选择、交叉、变异。
代码示例:旅行商问题
下面只放算法主类,城市、种群和路径实体可以自己实现:
package ClassicProblems.TSP_genetic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
// 遗传算法主类
public class TSPGeneticAlgorithmWithMatrix {
private static final double mutationRate = 0.015; // 变异率
private static final int tournamentSize = 5; // 锦标赛选择大小
private static final boolean elitism = true; // 是否保留最优个体
// 进化种群
public static Population evolvePopulation(Population pop, List<City> cities, double[][] distanceMatrix) {
Population newPopulation = new Population(pop.populationSize());
int elitismOffset = 0;
// 如果启用精英主义,则保留最优个体
if (elitism) {
newPopulation.saveTour(0, new Tour(pop.getFittest().tour, distanceMatrix)); // Deep copy of best tour
elitismOffset = 1;
}
// 通过交叉产生新后代
for (int i = elitismOffset; i < pop.populationSize(); i++) {
// 选择父母
Tour parent1 = tournamentSelection(pop, cities, distanceMatrix);
Tour parent2 = tournamentSelection(pop, cities, distanceMatrix);
// 交叉产生孩子
Tour child = crossover(parent1, parent2, distanceMatrix);
newPopulation.saveTour(i, child);
}
// 对新种群进行变异(跳过精英个体)
for (int i = elitismOffset; i < newPopulation.populationSize(); i++) {
mutate(newPopulation.getTour(i));
}
return newPopulation;
}
// 交叉操作 (顺序交叉 OX1) - 修复版
public static Tour crossover(Tour parent1, Tour parent2, double[][] distanceMatrix) {
Random rand = new Random();
// 处理边界情况:如果只有一个或没有城市,直接返回父代副本
if (parent1.tourSize() < 2) {
return new Tour(parent1.tour, distanceMatrix);
}
// 随机选择交叉点 (确保 start <= end,并且至少留一个位置用于交叉)
int start = rand.nextInt(parent1.tourSize());
// end 至少是 start + 1,最多是 tourSize - 1 int end = start + rand.nextInt(parent1.tourSize() - start);
// 如果需要 start < end 的严格小于关系,可以调整上面一行
// 例如: int end = start + 1 + rand.nextInt(parent1.tourSize() - start - 1);
// 但允许 start==end 也是可以的,代表只复制一个基因
// 为了确保 start <= end (如果上面的逻辑没保证)
if (start > end) {
int temp = start;
start = end;
end = temp;
}
// 1. 创建一个空的子代 Tour Tour child = new Tour(distanceMatrix);
// 2. *** 关键修复:预先填充 child 的 tour 列表,使其具有正确的大小 *** // 这里我们用 null 占位,稍后会被实际城市或保留片段填充
for (int i = 0; i < parent1.tourSize(); i++) {
child.tour.add(null); // 或者 child.setCity(i, null) 如果你更喜欢用 setCity }
// 3. 将 parent1 的 [start, end] 片段直接复制到 child 的相同位置
for (int i = start; i <= end; i++) {
child.setCity(i, parent1.getCity(i));
}
// 4. 从 parent2 获取城市顺序,并填充 child 中剩余的空位
int childIndex = 0;
for (int i = 0; i < parent2.tourSize(); i++) {
City cityFromParent2 = parent2.getCity(i);
// 如果这个城市不在 child 已经保留的片段中
if (!child.containsCity(cityFromParent2)) {
// 找到 child 中下一个可用的 (为 null 的) 位置
while (childIndex < child.tourSize() && child.getCity(childIndex) != null) {
childIndex++;
}
// 如果找到了位置,则放置城市
if (childIndex < child.tourSize()) {
child.setCity(childIndex, cityFromParent2);
// childIndex++; // 这行可以加也可以不加,下次循环开始时会继续找下一个null位
}
}
}
// 5. 返回构建好的子代
return child;
}
// 变异操作 (交换变异)
private static void mutate(Tour tour) {
Random rand = new Random();
for (int i = 0; i < tour.tourSize(); i++) {
if (rand.nextDouble() < mutationRate) {
int j = rand.nextInt(tour.tourSize());
City city1 = tour.getCity(i);
City city2 = tour.getCity(j);
tour.setCity(j, city1);
tour.setCity(i, city2);
}
}
}
// 锦标赛选择
private static Tour tournamentSelection(Population pop, List<City> cities, double[][] distanceMatrix) {
Population tournament = new Population(tournamentSize);
for (int i = 0; i < tournamentSize; i++) {
int randomId = (int) (Math.random() * pop.populationSize());
Tour selectedTour = pop.getTour(randomId);
// Ensure we don't add null tours to the tournament
if (selectedTour != null) {
tournament.saveTour(i, new Tour(selectedTour.tour, distanceMatrix)); // Copy
} else {
// If selected tour is null, create a random one for the tournament
Tour randomTour = new Tour(distanceMatrix).generateIndividual(cities);
tournament.saveTour(i, randomTour);
}
}
Tour fittest = tournament.getFittest();
return fittest;
}
public static void main(String[] args) {
// 定义城市 (这里仍然用ID来标识)
List<City> cities = new ArrayList<>();
int numCities = 5;
for (int i = 0; i < numCities; i++) {
cities.add(new City(i, "C" + i));
}
// 定义距离矩阵 (非对称示例)
// C0 C1 C2 C3 C4 double[][] distanceMatrix = {
{0, 10, 15, 20, 25}, // From C0
{12, 0, 8, 17, 22}, // From C1
{20, 25, 0, 9, 14}, // From C2
{10, 15, 20, 0, 11}, // From C3
{30, 35, 5, 15, 0} // From C4
};
System.out.println("城市列表: " + cities);
System.out.println("城市数量: " + cities.size());
System.out.println("距离矩阵:");
for (int i = 0; i < distanceMatrix.length; i++) {
System.out.println(Arrays.toString(distanceMatrix[i]));
}
// 初始化种群
int populationSize = 20;
Population population = new Population(populationSize, 0); // Use dummy constructor
// 手动填充初始种群
for (int i = 0; i < populationSize; i++) {
Tour newTour = new Tour(distanceMatrix).generateIndividual(cities);
population.saveTour(i, newTour);
}
System.out.println("\n初始种群最优距离: " + population.getFittest().getDistance());
// 进化种群
int numberOfGenerations = 500;
for (int i = 0; i < numberOfGenerations; i++) {
population = evolvePopulation(population, cities, distanceMatrix);
if (i % 100 == 0) { // 每100代打印一次信息
Tour currentBest = population.getFittest();
if (currentBest != null) {
System.out.println("第 " + i + " 代 最优距离: " + currentBest.getDistance());
}
}
}
System.out.println("\n--- 进化完成 ---");
Tour finalBest = population.getFittest();
if (finalBest != null) {
System.out.println("最终最优距离: " + finalBest.getDistance());
System.out.println("最终最优路径: ");
System.out.println(finalBest);
} else {
System.out.println("未能找到有效路径。");
}
}
}

浙公网安备 33010602011771号