摘自:https://www.zhihu.com/question/23293449
什么是遗传算法呢?
简单地说,遗传算法是一种解决问题的方法。它模拟大自然中种群在选择压力下的演化,从而得到问题的一个近似解。
实际上,在生活和生产中,很多时候并不需要得到一个完美的答案;而很多问题如果要得到完美的答案的话,需要很大量的计算。所以,因为遗传算法能在相对较短的时间内给出一个足够好能凑合的答案。
缺点:早期的优势基因可能会很快通过交换基因的途径散播到整个种群中,这样有可能导致早熟(premature),也就是说整个种群的基因过早同一化,得不到足够好的结果。这个问题是难以完全避免的。
像这样可以适应很多问题的算法还有模拟退火算法、粒子群算法、蚁群算法、禁忌搜索等等,统称为元启发式算法(Meta-heuristic algorithms)。
另外,基于自然演化过程的算法除了在这里说到的遗传算法以外,还有更广泛的群体遗传算法和遗传编程等,它们能解决很多棘手的问题。这也从一个侧面说明,我们不一定需要一个智能才能得到一个构造精巧的系统。
遗传算法模拟一个人工种群的进化过程,通过选择(Selection)、交叉(Crossover)以及变异(Mutation)等机制,在每次迭代中都保留一组候选个体,重复此过程,种群经过若干代进化后,理想情况下其适应度达到近似最优的状态。
遗传算法组成
- 个体 -> 种群
- 适应度函数
- 遗传算子
- 选择(轮盘赌选择方法,精英机制)
- 交叉
- 变异(保持种群内的多样性,并防止过早收敛)
运行参数
- 是否选择精英操作
- 种群大小
- 染色体长度
- 最大迭代次数
- 交叉概率
- 变异概率
实数编码和二进制编码
java和python实现的demo,注意:求解的是两个不同的问题。
java实现的demo:
import java.util.Random;
/** * * @author Vijini * 链接:https://www.zhihu.com/question/23293449/answer/203471391 * 说明:给定一组五个基因,每一个基因可以保存一个二进制值 0 或 1。 * 这里的适应度是基因组中 1 的数量。 * 如果基因组内共有五个 1,则该个体适应度达到最大值。 * 如果基因组内没有 1,那么个体的适应度达到最小值。 * 该遗传算法希望最大化适应度,并提供适应度达到最大的个体所组成的群体。 * 注意:本例中,在交叉运算与突变运算之后,适应度最低的个体被新的,适应度最高的后代所替代。 * */
//Main class
public class SimpleDemoGA {
Population population = new Population();
Individual fittest;
Individual secondFittest;
int generationCount = 0;
public static void main(String[] args) {
Random rn = new Random();
SimpleDemoGA demo = new SimpleDemoGA();
//Initialize population
demo.population.initializePopulation(10);
//Calculate fitness of each individual
demo.population.calculateFitness();
System.out.println("Generation: " + demo.generationCount + " Fittest: " + demo.population.fittest);
//While population gets an individual with maximum fitness
while (demo.population.fittest < 5) {
++demo.generationCount;
//Do selection
demo.selection();
//Do crossover
demo.crossover();
//Do mutation under a random probability
if (rn.nextInt()%7 < 5) {
demo.mutation();
}
//Add fittest offspring to population
demo.addFittestOffspring();
//Calculate new fitness value
demo.population.calculateFitness();
System.out.println("Generation: " + demo.generationCount + " Fittest: " + demo.population.fittest);
}
System.out.println("\nSolution found in generation " + demo.generationCount);
System.out.println("Fitness: "+demo.population.getFittest().fitness);
System.out.print("Genes: ");
for (int i = 0; i < 5; i++) {
System.out.print(demo.population.getFittest().genes[i]);
}
System.out.println("");
}
//Selection
void selection() {
//Select the most fittest individual
fittest = population.getFittest();
//Select the second most fittest individual
secondFittest = population.getSecondFittest();
}
//Crossover
void crossover() {
Random rn = new Random();
//Select a random crossover point
int crossOverPoint = rn.nextInt(population.individuals[0].geneLength);
//Swap values among parents
for (int i = 0; i < crossOverPoint; i++) {
int temp = fittest.genes[i];
fittest.genes[i] = secondFittest.genes[i];
secondFittest.genes[i] = temp;
}
}
//Mutation
void mutation() {
Random rn = new Random();
//Select a random mutation point
int mutationPoint = rn.nextInt(population.individuals[0].geneLength);
//Flip values at the mutation point
if (fittest.genes[mutationPoint] == 0) {
fittest.genes[mutationPoint] = 1;
} else {
fittest.genes[mutationPoint] = 0;
}
mutationPoint = rn.nextInt(population.individuals[0].geneLength);
if (secondFittest.genes[mutationPoint] == 0) {
secondFittest.genes[mutationPoint] = 1;
} else {
secondFittest.genes[mutationPoint] = 0;
}
}
//Get fittest offspring
Individual getFittestOffspring() {
if (fittest.fitness > secondFittest.fitness) {
return fittest;
}
return secondFittest;
}
//Replace least fittest individual from most fittest offspring
void addFittestOffspring() {
//Update fitness values of offspring
fittest.calcFitness();
secondFittest.calcFitness();
//Get index of least fit individual
int leastFittestIndex = population.getLeastFittestIndex();
//Replace least fittest individual from most fittest offspring
population.individuals[leastFittestIndex] = getFittestOffspring();
}
}
//Individual class
class Individual {
int fitness = 0;
int[] genes = new int[5];
int geneLength = 5;
public Individual() {
Random rn = new Random();
//Set genes randomly for each individual
for (int i = 0; i < genes.length; i++) {
genes[i] = rn.nextInt() % 2;
}
fitness = 0;
}
//Calculate fitness
public void calcFitness() {
fitness = 0;
for (int i = 0; i < 5; i++) {
if (genes[i] == 1) {
++fitness;
}
}
}
}
//Population class
class Population {
int popSize = 10;
Individual[] individuals = new Individual[10];
int fittest = 0;
//Initialize population
public void initializePopulation(int size) {
for (int i = 0; i < individuals.length; i++) {
individuals[i] = new Individual();
}
}
//Get the fittest individual
public Individual getFittest() {
int maxFit = Integer.MIN_VALUE;
for (int i = 0; i < individuals.length; i++) {
if (maxFit <= individuals[i].fitness) {
maxFit = i;
}
}
fittest = individuals[maxFit].fitness;
return individuals[maxFit];
}
//Get the second most fittest individual
public Individual getSecondFittest() {
int maxFit1 = 0;
int maxFit2 = 0;
for (int i = 0; i < individuals.length; i++) {
if (individuals[i].fitness > individuals[maxFit1].fitness) {
maxFit2 = maxFit1;
maxFit1 = i;
} else if (individuals[i].fitness > individuals[maxFit2].fitness) {
maxFit2 = i;
}
}
return individuals[maxFit2];
}
//Get index of least fittest individual
public int getLeastFittestIndex() {
int minFit = 0;
for (int i = 0; i < individuals.length; i++) {
if (minFit >= individuals[i].fitness) {
minFit = i;
}
}
return minFit;
}
//Calculate fitness of each individual
public void calculateFitness() {
for (int i = 0; i < individuals.length; i++) {
individuals[i].calcFitness();
}
getFittest();
}
}
python实现的demo
#coding=utf-8
# 来源:https://www.zhihu.com/question/23293449/answer/120530793
# 求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值。
import math
import random
class GA():
def __init__(self, length, count):
# 染色体长度
self.length = length
# 种群中的染色体数量
self.count = count
# 随机生成初始种群
self.population = self.gen_population(length, count)
def evolve(self, retain_rate=0.2, random_select_rate=0.5, mutation_rate=0.01):
""" 进化 对当前一代种群依次进行选择、交叉并生成新一代种群,然后对新一代种群进行变异 """
parents = self.selection(retain_rate, random_select_rate)
self.crossover(parents)
self.mutation(mutation_rate)
def gen_chromosome(self, length):
""" 随机生成长度为length的染色体,每个基因的取值是0或1 这里用一个bit表示一个基因 """
chromosome = 0
for i in range(length):
chromosome |= (1 << i) * random.randint(0, 1)
return chromosome
def gen_population(self, length, count):
""" 获取初始种群(一个含有count个长度为length的染色体的列表) """
return [self.gen_chromosome(length) for i in range(count)]
def fitness(self, chromosome):
""" 计算适应度,将染色体解码为0~9之间数字,代入函数计算 因为是求最大值,所以数值越大,适应度越高 """
x = self.decode(chromosome)
return x + 10*math.sin(5*x) + 7*math.cos(4*x)
def selection(self, retain_rate, random_select_rate):
""" 选择 先对适应度从大到小排序,选出存活的染色体 再进行随机选择,选出适应度虽然小,但是幸存下来的个体 """
# 对适应度从大到小进行排序
graded = [(self.fitness(chromosome), chromosome) for chromosome in self.population]
graded = [x[1] for x in sorted(graded, reverse=True)]
# 选出适应性强的染色体
retain_length = int(len(graded) * retain_rate)
parents = graded[:retain_length]
# 选出适应性不强,但是幸存的染色体
for chromosome in graded[retain_length:]:
if random.random() < random_select_rate:
parents.append(chromosome)
return parents
def crossover(self, parents):
""" 染色体的交叉、繁殖,生成新一代的种群 """
# 新出生的孩子,最终会被加入存活下来的父母之中,形成新一代的种群。
children = []
# 需要繁殖的孩子的量
target_count = len(self.population) - len(parents)
# 开始根据需要的量进行繁殖
while len(children) < target_count:
male = random.randint(0, len(parents)-1)
female = random.randint(0, len(parents)-1)
if male != female:
# 随机选取交叉点
cross_pos = random.randint(0, self.length)
# 生成掩码,方便位操作
mask = 0
for i in range(cross_pos):
mask |= (1 << i)
male = parents[male]
female = parents[female]
# 孩子将获得父亲在交叉点前的基因和母亲在交叉点后(包括交叉点)的基因
child = ((male & mask) | (female & ~mask)) & ((1 << self.length) - 1)
children.append(child)
# 经过繁殖后,孩子和父母的数量与原始种群数量相等,在这里可以更新种群。
self.population = parents + children
def mutation(self, rate):
""" 变异 对种群中的所有个体,随机改变某个个体中的某个基因 """
for i in range(len(self.population)):
if random.random() < rate:
j = random.randint(0, self.length-1)
self.population[i] ^= 1 << j
def decode(self, chromosome):
""" 解码染色体,将二进制转化为属于[0, 9]的实数 """
return chromosome * 9.0 / (2**self.length-1)
def result(self):
""" 获得当前代的最优值,这里取的是函数取最大值时x的值。 """
graded = [(self.fitness(chromosome), chromosome) for chromosome in self.population]
graded = [x[1] for x in sorted(graded, reverse=True)]
return ga.decode(graded[0])
if __name__ == '__main__':
# 染色体长度为17, 种群数量为300
ga = GA(17, 300)
# 200次进化迭代
for x in range(200):
ga.evolve()
print(ga.result())