1.什么是遗传算法
遗传算法(Genetic Algorithm,简称GA)是一种基于生物进化论和遗传学原理的全局优化搜索算法。它通过模拟自然界中生物种群的遗传机制和进化过程来解决复杂问题,如函数优化、组合优化、机器学习等。遗传算法具有并行性、全局搜索能力和对问题描述的简单性,在很多领域有着广泛应用。
2.遗传算法的基本原理
遗传算法的基本思想是将问题的解表示为“染色体”(Chromosome),每个染色体由一系列“基因”(Gene)组成,这些基因可以是二进制编码、实数或离散值。算法通过选择、交叉(也称为杂交)、变异等遗传操作,模仿自然选择和遗传变异的过程,使种群中的个体逐渐适应环境,即找到问题的最优解或近似最优解。
下面我试着用通俗一点的例子来解释什么是“遗传算法”:
想象一下,在一座起伏的山脉中,住着一群人。这些人散居在山脉的不同高度上,有的住在山顶,有的住在半山腰,还有的住在山谷里。突然有一天,洪水来袭,水位不断上升,那些住在低处的人们开始面临危险。但住在高处的人因为地势较高,所以能够幸免于难,我们可以说,这些人对这种“洪水”环境的适应能力更强。
为了确保人类的延续,这些适应能力强的人开始繁衍后代。他们的目标是希望通过繁殖,能够产生出生活在山脉最高点的后代,这样就能完全避免洪水的威胁,相当于找到了最完美的生存策略。在这个过程中,父母双方的特征(我们称之为“基因”)会通过一种类似“混合”的方式传递给子女,有时候,子女身上还会出现一些意料之外的新特征,这就是“基因变异”。
遗传算法就像是指导这群人在面对洪水时如何繁衍出能在最高山峰生存的后代的一种智慧。它通过模拟自然界中的选择、交配和变异过程,帮助寻找出最适应环境的解决方案。在这个例子中,“最高山峰”的横坐标就是我们要找的答案,也就是那组能让人们在任何情况下都能生存下来的最优“DNA”。
即变成了求解该函数在区间[0 , 5]上的最大值点的问题。
简而言之,遗传算法就像是一种搜索策略,它通过模仿自然进化的过程,逐步改进并找到解决问题的最佳方案。在这个过程中,那些表现更好(适应度更高)的解决方案会被优先保留和进一步优化,直到找到最优解。
3.遗传算法的名词解释
-
种群(Population):遗传算法中的一组可能解的集合。每个解都是一个染色体。
-
染色体(Chromosome):代表一个可能的解,由多个基因组成。
-
基因(Gene):染色体上的基本单元,通常是一个二进制位或数值,表示解的一个部分。
-
适应度函数(Fitness Function):用于评估染色体的好坏,即解的质量。适应度高的染色体有更大的机会被选中进行遗传操作。
-
选择(Selection):根据适应度函数,从当前种群中选择优秀的个体进入下一代种群的过程。常用的选择方法有轮盘赌选择、锦标赛选择等。
-
交叉(Crossover):两个染色体交换部分基因,生成新的染色体。这是遗传算法中实现全局搜索的主要方式。
-
变异(Mutation):以很小的概率改变染色体中的某个基因,增加种群的多样性,防止算法过早收敛。
-
收敛(Convergence):当种群中的个体适应度不再显著提高时,认为算法已经找到最优解或近似最优解,此时算法结束。
遗传算法通过迭代上述过程,不断优化种群,最终找到满足要求的解。
4.遗传算法程序实现过程
遗传算法实现的一般流程如下:
(1)初始化种群
生成一个初始种群,种群中的每一个个体(染色体)都代表了一个可能的解。
每个个体由一组基因构成,基因的编码形式取决于问题的特性,可以是二进制、实数或符号编码等。
(2)评估适应度
使用适应度函数(Fitness Function)来评估每个个体的适应度,这反映了个体在解决特定问题上的性能。
适应度函数应该设计得能够量化个体解的质量,以便于比较不同个体的优劣。
(3)选择(Selection)
根据个体的适应度值进行选择,适应度较高的个体有更高的概率被选中参与后续的遗传操作。
常见的选择策略包括轮盘赌选择、锦标赛选择、排名选择等。
(4)遗传操作
交叉(Crossover):选定的个体之间进行基因信息的交换,以产生新的后代个体。
变异(Mutation):以小概率随机改变后代个体的某些基因,以增加种群的多样性。
这些操作通常遵循一定的概率规则,例如交叉概率和变异概率。
(5)替换(Replacement)
新产生的后代个体可能会替代旧的种群成员,这个过程可以通过多种策略进行,如世代替换、稳态遗传算法等。
有时会使用精英策略(Elitism),确保种群中最好的个体被保留到下一代。
(6)终止条件检查
检查是否达到预定的终止条件,这可能是固定的迭代次数、种群的平均适应度或最佳适应度达到某个阈值。
如果满足终止条件,则算法停止;否则,继续回到步骤3。
(7)输出结果
算法结束后,输出种群中适应度最高的个体作为最优解。
5.遗传算法代码实现
5.1python代码实现
种群类设计:
import numpy as np
# 定义种群类
class Population:
def __init__(self, pop_size, chromosome_size):
# 初始化种群大小和染色体大小
self.pop_size = pop_size
self.chromosome_size = chromosome_size
# 随机生成初始种群,每个基因是0或1
self.population = np.round(np.random.rand(pop_size, chromosome_size)).astype(np.int)
# 初始化适应度值为0
self.fit_value = np.zeros((pop_size, 1))
# 选择染色体方法
def select_chromosome(self):
# 计算总适应度值
total_fitness_value = self.fit_value.sum()
# 计算每个个体的选择概率
p_fit_value = self.fit_value / total_fitness_value
# 计算累积概率
p_fit_value = np.cumsum(p_fit_value)
# 生成随机点,用于轮盘赌选择
point = np.sort(np.random.rand(self.pop_size, 1), 0)
# 初始化选择和新的种群索引
fit_in = 0
new_in = 0
new_population = np.zeros_like(self.population)
# 轮盘赌选择新种群
while new_in < self.pop_size:
if point[new_in] < p_fit_value[fit_in]:
new_population[new_in, :] = self.population[fit_in, :]
new_in += 1
else:
fit_in += 1
# 更新种群
self.population = new_population
# 交叉染色体方法
def cross_chromosome(self, cross_rate):
# 获取种群大小和染色体大小
x = self.pop_size
y = self.chromosome_size
new_population = np.zeros_like(self.population)
# 对种群中的染色体进行交叉操作
for i in range(0, x-1, 2):
# 根据交叉率决定是否交叉
if np.random.rand(1) < cross_rate:
# 生成交叉点
insert_point = int(np.round(np.random.rand(1) * y).item())
# 交叉染色体
new_population[i, :] = np.concatenate([self.population[i, 0:insert_point], self.population[i+1, insert_point:y]], 0)
new_population[i+1, :] = np.concatenate([self.population[i+1, 0:insert_point], self.population[i, insert_point:y]], 0)
else:
# 不交叉则直接复制
new_population[i, :] = self.population[i, :]
new_population[i + 1, :] = self.population[i + 1, :]
# 更新种群
self.population = new_population
# 寻找最佳个体方法
def best(self):
# 初始化最佳个体和适应度值
best_individual = self.population[0, :]
best_fit = self.fit_value[0]
# 遍历种群寻找最佳个体
for i in range(1, self.pop_size):
if self.fit_value[i] > best_fit:
best_individual = self.population[i, :]
best_fit = self.fit_value[i]
return best_individual, best_fit
# 突变染色体方法
def mutation_chromosome(self, mutation_rate):
# 获取种群大小
x = self.pop_size
# 对种群中的染色体进行突变操作
for i in range(x):
if np.random.rand(1) < mutation_rate:
# 生成突变点
m_point = int(np.round(np.random.rand(1) * self.chromosome_size).item())
# 突变操作
if self.population[i, m_point] == 1:
self.population[i, m_point] = 0
else:
self.population[i, m_point] = 1
# 二进制转十进制方法
def binary2decimal(self, population):
pop1 = np.zeros_like(population)
y = self.chromosome_size
# 转换二进制到十进制
for i in range(y):
pop1[:, i] = 2 ** (y - i - 1) * population[:, i]
pop = np.sum(pop1, 1)
pop2 = pop * 10 / (1 << y)
return pop2
# 计算目标函数值方法
def cal_obj_value(self):
# 将二进制表示转换为十进制表示
x = self.binary2decimal(self.population)
# 计算并更新适应度值
self.fit_value = 10 * np.sin(5 * x) + 7 * np.abs(x - 5) +
测试:
from GA遗传算法.Population import Population
import numpy as np
cross_rate = 0.6
mutation_rate = 0.001
pop_size = 100
chromosome_size = 10
population = Population(100, 10)
for i in range(100):
population.cal_obj_value()
population.select_chromosome()
population.cross_chromosome(cross_rate)
population.mutation_chromosome(mutation_rate)
best_individual, best_fit = population.best()
best_individual = np.expand_dims(best_individual, 0)
x = population.binary2decimal(best_individual)
print("X:", x, "\t Y: ", best_fit)
5.2java代码
import java.util.ArrayList;
import java.util.List;
public abstract class GeneticAlgorithm {
private List<Chromosome> population = new ArrayList<>();
private int popSize = 100;//种群数量
private int geneSize;//基因最大长度
private int maxIterNum = 500;//最大迭代次数
private double crossRate = 0.6;
private double mutationRate = 0.01;//基因变异的概率
private int maxMutationNum = 3;//最大变异次数
private int generation = 1;//当前遗传到第几代
private double bestScore;//最好得分 局部
private double worstScore;//最坏得分 局部
private double totalScore;//总得分 局部
private double averageScore;//平均得分
private double x; //记录历史种群中最好的X值
private double y; //记录历史种群中最好的Y值
private int geneI;//x y所在代数
public GeneticAlgorithm(int geneSize) {
this.geneSize = geneSize;
}
public void calculate(){
generation = 1;
init();
while (generation < maxIterNum) { //迭代maxIterNum
evolve(); // (选择 -> 交叉)+ -> 变异 -> 计算得分
print(); // 打印
generation++; // 代数
}
}
private void evolve() {
List<Chromosome> childPopulation = new ArrayList<>();
while (childPopulation.size() < popSize) {
Chromosome p1 = getParentChromosome();
Chromosome p2 = getParentChromosome();
List<Chromosome> chromosomes = Chromosome.genetic(p1, p2, crossRate);
if (chromosomes != null)
childPopulation.addAll(chromosomes);
}
List<Chromosome> t = population;
population = childPopulation;
t.clear();
t = null;
mutation();
calculateScore();
}
private void init(){
population = new ArrayList<>();
for (int i = 0; i < popSize; i++) {
Chromosome chromosome = new Chromosome(geneSize);
population.add(chromosome);
}
calculateScore();
}
/**
* 选择过程:轮盘赌法
* @return
*/
private Chromosome getParentChromosome() {
double slide = Math.random() * totalScore;
double sum = 0;
for (Chromosome chromosome : population) {
sum += chromosome.getScore();
if (slide < sum && chromosome.getScore() >= averageScore)
return chromosome;
}
return null;
}
private void calculateScore() {
setChromosomeScore(population.get(0));
bestScore = population.get(0).getScore();
worstScore = population.get(0).getScore();
totalScore = 0;
for (Chromosome chromosome : population) {
setChromosomeScore(chromosome);
if (chromosome.getScore() > bestScore) {
bestScore = chromosome.getScore();
if (y < bestScore) {
x = changeX(chromosome);
y = bestScore;
geneI = geneSize;
}
}
if (chromosome.getScore() < worstScore)
worstScore = chromosome.getScore();
totalScore += chromosome.getScore();
}
averageScore = totalScore / popSize;
averageScore = Math.min(averageScore, bestScore);
}
private void mutation() {
for (Chromosome chromosome : population) {
if (Math.random() < mutationRate)
chromosome.mutation((int) (Math.random() * maxMutationNum)); //变异次数
}
}
private void setChromosomeScore(Chromosome chromosome) {
if (chromosome == null) {
return;
}
double x = changeX(chromosome);
double y = calculateY(x);
chromosome.setScore(y);
}
private void print() {
System.out.println("--------------------------------");
System.out.println("the generation is:" + generation);
System.out.println("the best y is:" + bestScore);
System.out.println("the worst fitness is:" + worstScore);
System.out.println("the average fitness is:" + averageScore);
System.out.println("the total fitness is:" + totalScore);
System.out.println("geneI:" + geneI + "\tx:" + x + "\ty:" + y);
}
public abstract double calculateY(double x);
public abstract double changeX(Chromosome chromosome);
public List<Chromosome> getPopulation() {
return population;
}
public void setPopulation(List<Chromosome> population) {
this.population = population;
}
public int getPopSize() {
return popSize;
}
public void setPopSize(int popSize) {
this.popSize = popSize;
}
public int getGeneSize() {
return geneSize;
}
public void setGeneSize(int geneSize) {
this.geneSize = geneSize;
}
public int getMaxIterNum() {
return maxIterNum;
}
public void setMaxIterNum(int maxIterNum) {
this.maxIterNum = maxIterNum;
}
public double getMutationRate() {
return mutationRate;
}
public void setMutationRate(double mutationRate) {
this.mutationRate = mutationRate;
}
public int getMaxMutationNum() {
return maxMutationNum;
}
public void setMaxMutationNum(int maxMutationNum) {
this.maxMutationNum = maxMutationNum;
}
public int getGeneration() {
return generation;
}
public void setGeneration(int generation) {
this.generation = generation;
}
public double getBestScore() {
return bestScore;
}
public void setBestScore(double bestScore) {
this.bestScore = bestScore;
}
public double getWorstScore() {
return worstScore;
}
public void setWorstScore(double worstScore) {
this.worstScore = worstScore;
}
public double getTotalScore() {
return totalScore;
}
public void setTotalScore(double totalScore) {
this.totalScore = totalScore;
}
public double getAverageScore() {
return averageScore;
}
public void setAverageScore(double averageScore) {
this.averageScore = averageScore;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public int getGeneI() {
return geneI;
}
public void setGeneI(int geneI) {
this.geneI = geneI;
}
}
5.3结果展示
在某些情况下遗传算法可能陷入局部最优。
1. 早熟收敛(Premature Convergence)
原因:种群中的个体过早地变得相似,导致遗传多样性下降,使得算法无法探索解空间的其他区域。
表现:种群的适应度值在算法早期迅速上升,但随后停滞不前。
2. 选择压力过大
原因:选择操作过于偏向适应度高的个体,可能导致适应度较低的个体被过早淘汰,减少了种群的多样性。
表现:算法快速收敛到某个局部最优解,而忽略了可能存在更好解的区域。
3. 交叉和突变概率设置不当
原因:交叉率和突变率太低,无法有效地探索新的解空间;交叉率和突变率太高,可能导致算法变成随机搜索。
表现:交叉和突变不足时,算法容易在局部最优解附近徘徊;过度交叉和突变则可能导致算法无法收敛。
4. 适应度函数设计不当
原因:适应度函数未能准确反映解的质量,或者存在多个局部最优解,而适应度函数无法有效区分。
表现:算法可能收敛到非目标解的局部最优解。
5. 解的表示方法
原因:解的编码方式可能不适合特定问题,导致算法难以找到全局最优解。
表现:即使算法能够探索解空间,但由于编码的限制,仍可能无法表示全局最优解。
6. 种群大小
原因:种群大小太小,无法维持足够的遗传多样性;种群大小太大,计算成本过高。
表现:种群太小容易陷入局部最优,种群太大则可能导致算法效率低下。
7. 算法停止条件
原因:算法停止条件设置过早,可能导致算法未能充分探索解空间。
表现:算法在达到停止条件时可能刚好处于局部最优解。