遗传算法是一种基于达尔文"适者生存"理论和孟德尔遗传规律的计算模型。它通过模拟生物进化的过程,在种群中进行选择、交叉和变异操作,从而逐步优化目标函数。遗传算法的核心思想是通过群体代的方式,不断生成更适应环境的个体,最终找到最优解或接近最优解的个体。
算法的作用
遗传算法主要用于解决优化问题,尤其是在传统优化方法难以奏效的情况下。它通过模拟自然界中的进化过程,逐步逼近最优解或满意解。
算法步骤
1. 种群初始化
定义:
种群初始化是算法开始时,随机或者按照某种规则生成一组候选解。
初始化方法:
- 随机初始化:
最常见的方法,通过随机生成满足约束条件的个体来构建初始种群。这种方法简单易实现,但种群质量一般不高。 - 启发式初始化:
结合问题领域的先验知识或启发式规则生成初始种群,从而提高种群的质量和收敛速度。然而,对于某些问题可能产生经验性错误。 - 混合初始化:
将随机初始化和启发式初始化结合,可以平衡种群多样性和质量。
种群大小:
根据问题规模调整种群大小。较大的种群可以提供更好的搜索能力,但也增加了计算成本,因此需要在两者之间进行权衡。
2. 编码
定义:
编码是将问题的解表示为遗传算法可操作的形式。
常见的编码方式:
- 二进制编码:
将解表示为二进制字符串。优点是实现简单,易于进行交叉和变异操作;缺点是可能存在精度缺失和编码长度过长的问题。 - 实数编码:
直接用实数表示解的各个维度(如向量问题)。优点是没有精度问题,适合连续优化问题;缺点是交叉和变异操作相对复杂。 - 排列编码:
用于解决排列问题。优点是适合解决排列相关问题;缺点是需要特殊的设计以保证解的合法性。 - 字符编码:
使用字符或符号表示解。 - 混合编码:
结合多种编码方式,适用于复杂的优化问题。
编码选择的影响:
不同的编码方式对遗传算法的操作(如交叉、变异)有不同的适应性,需根据具体问题选择合适的编码方式。
3. 适应度计算
定义:
适应度是对遗传算法中每个个体的"优劣"进行量化评估的一个数值。
- 适应度函数:
衡量个体优劣的标准,通常是目标函数的直接或间接映射。适应度值越高,表示该个体越优秀。
4. 选择
定义:
选择是从当前种群中挑选出优秀的个体。
常见的选择方式:
- 轮盘赌选择:
根据个体的适应度值分配一个"选择概率",适应度越高,被选中的概率越大。缺点是可能导致种群中某些个体占据主导地位,降低种群多样性。 - 锦标赛选择:
随机从种群中抽取若干个个体(称为一个小的"锦标赛"),从中选择适应度最高的个体。优点是简单高效,且可以通过调整锦标赛规模控制选择压力。 - 排名选择:
根据个体的适应度排名而非绝对适应度值来分配选择概率,排名越高,被选中的概率越大。优点是避免了适应度值差距过大导致的选择压力过高问题。 - 随机遍历抽样:
在轮盘赌选择的基础上改进,通过等间距采样提高效率并减少随机性带来的偏差。 - 精英保留策略:
直接将当前种群中适应度最高的个体复制到下一代,确保优秀解不会因遗传操作而丢失。通常与其他选择方法结合使用。
5. 交叉
定义:
交叉是将两个父代个体的部分基因交换,生成新的子代个体。
常见的交叉方式:
- 单点交叉:
在随机位置交换两个父代的部分基因。 - 多点交叉:
在多个位置交换基因。 - 均匀交叉:
每个基因位独立以一定概率决定是否交换。 - 算术交叉: 对实数编码的父代基因进行线性组合 (demo使用的就是这种,简单)
6. 变异
定义:
变异是对个体的某些基因进行随机改变,增加种群的多样性。变异的概率通常较低。
7. 终止条件
遗传算法的终止条件包括但不限于以下几种:
- 个体的适应度达到给定的阈值。
- 最优个体适应度和群体适应度不再上升。
- 迭代次数达到预设的代数。
算法简单案例
以下是一个简单的遗传算法实现,用于求解函数 f(x) = x + 10 * sin(5x) + 7 * cos(4x)
的最大值。
scss
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class GeneticAlgorithm {
// 目标函数
public static double fitnessFunction(double x) {
return x + 10 * Math.sin(5 * x) + 7 * Math.cos(4 * x);
}
// 初始化种群
public static List<Double> initializePopulation(int popSize, double lowerBound, double upperBound) {
List<Double> population = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < popSize; i++) {
population.add(lowerBound + random.nextDouble() * (upperBound - lowerBound));
}
return population;
}
// 轮盘赌选择
public static List<Double> selection(List<Double> population, List<Double> fitnesses) {
double totalFitness = 0;
for (double fitness : fitnesses) {
totalFitness += fitness;
}
List<Double> selectedParents = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 2; i++) {
double r = random.nextDouble() * totalFitness;
double cumulativeFitness = 0;
for (int j = 0; j < population.size(); j++) {
cumulativeFitness += fitnesses.get(j);
if (cumulativeFitness >= r) {
selectedParents.add(population.get(j));
break;
}
}
}
return selectedParents;
}
// 算术交叉
public static List<Double> crossover(List<Double> parents, double alpha) {
double parent1 = parents.get(0);
double parent2 = parents.get(1);
double child1 = alpha * parent1 + (1 - alpha) * parent2;
double child2 = (1 - alpha) * parent1 + alpha * parent2;
List<Double> children = new ArrayList<>();
children.add(child1);
children.add(child2);
return children;
}
// 高斯变异
public static double mutation(double individual, double mutationRate, double stdDev, double lowerBound, double upperBound) {
Random random = new Random();
if (random.nextDouble() < mutationRate) {
individual += random.nextGaussian() * stdDev;
individual = Math.max(lowerBound, Math.min(individual, upperBound)); // 确保个体在定义域内
}
return individual;
}
// 遗传算法主函数
public static void geneticAlgorithm(int popSize, int generations, double mutationRate, double alpha) {
double lowerBound = 0, upperBound = 10;
double precision = 0.01;
// 初始化种群
List<Double> population = initializePopulation(popSize, lowerBound, upperBound);
for (int generation = 0; generation < generations; generation++) {
// 计算适应度值
List<Double> fitnesses = new ArrayList<>();
for (double individual : population) {
fitnesses.add(fitnessFunction(individual));
}
// 找到当前最优解
double bestFitness = Double.MIN_VALUE;
double bestIndividual = 0;
for (int i = 0; i < fitnesses.size(); i++) {
if (fitnesses.get(i) > bestFitness) {
bestFitness = fitnesses.get(i);
bestIndividual = population.get(i);
}
}
System.out.printf("Generation %d: Best x = %.4f, Fitness = %.4f%n", generation, bestIndividual, bestFitness);
// 新一代种群
List<Double> newPopulation = new ArrayList<>();
while (newPopulation.size() < popSize) {
// 选择
List<Double> parents = selection(population, fitnesses);
// 交叉
List<Double> offspring = crossover(parents, alpha);
// 变异
for (double child : offspring) {
double mutatedChild = mutation(child, mutationRate, precision, lowerBound, upperBound);
newPopulation.add(mutatedChild);
}
}
// 更新种群(保留最优解)
population = newPopulation.subList(0, popSize);
population.set(0, bestIndividual); // 强制保留最优解
}
// 输出最终结果
double finalBestFitness = Double.MIN_VALUE;
double finalBestIndividual = 0;
for (double individual : population) {
double fitness = fitnessFunction(individual);
if (fitness > finalBestFitness) {
finalBestFitness = fitness;
finalBestIndividual = individual;
}
}
System.out.printf("Final Result: Best x = %.4f, Fitness = %.4f%n", finalBestIndividual, finalBestFitness);
}
public static void main(String[] args) {
int popSize = 50; // 种群大小
int generations = 100; // 迭代次数
double mutationRate = 0.1; // 变异率
double alpha = 0.6; // 交叉参数
geneticAlgorithm(popSize, generations, mutationRate, alpha);
}
}
算法总结
遗传算法通过模拟生物进化机制,为解决复杂优化问题提供了有效途径。其核心特点包括:
- 群体搜索策略: 避免单点搜索的局限性。
- 优胜劣汰机制: 保证搜索方向的有效性。
- 随机操作组合: 平衡探索与开发。
遗传算法具有较强的鲁棒性和通用性,适用于多种类型的优化问题。但在实际应用中,需要根据问题的特点调整参数和操作方式,以获得最佳性能。