遗传算法:代码世界的 “进化传奇”
在神奇的大自然中,生物们为了适应环境,不断进化。遗传算法就像是把生物进化的这套 “生存秘籍” 偷学到了代码世界里。想象一下,代码也能像生物一样 “生孩子”“变异”,最后进化出解决复杂问题的超能力,是不是很神奇?
算法原理:代码版的 “生物进化”
遗传算法的核心思想来源于达尔文的进化论,简单来说就是 “物竞天择,适者生存”。在遗传算法里,我们把问题的每个可能解看作一个 “生物个体”,这些个体组成了一个 “种群”。
编码:给代码 “穿新衣”
首先,要把每个解(也就是个体)进行编码,就好比给生物穿上一件特别的 “衣服”,这件 “衣服” 上的信息代表了解的特征。最常见的编码方式是二进制编码,比如用一串 0 和 1 来表示一个个体。假如我们要求解一个简单的数学问题,比如找到一个在 0 到 15 之间的整数 x,使得 x 的平方最接近 100。我们可以用 4 位二进制数来编码 x,0000 表示 0,0001 表示 1,以此类推。
适应度函数:评判 “生存能力”
每个个体都有一个适应度,这就像是生物在自然界中的生存能力。适应度函数用来计算每个个体的适应度,对于刚才找最接近 100 的平方数问题,适应度函数可以是 100 减去 x 平方的绝对值,这个值越小,说明个体的适应度越高,也就是这个解越好。
选择:“强者生存”
接下来是选择阶段,就像大自然中强者生存一样,适应度高的个体有更大的概率被选中,进入下一代。常用的选择方法有轮盘赌选择法,把每个个体想象成轮盘上的一块区域,适应度越高,这块区域就越大,被选中的概率也就越大。
交叉:“生孩子”
被选中的个体要 “生孩子” 啦,这就是交叉操作。比如有两个个体 A(0011)和 B(1100),随机选择一个交叉点,假设是第 2 位,那么交叉后就产生两个新个体 A'(0000)和 B'(1111),这两个新个体就进入了下一代种群。
变异:“小调皮搞破坏”
变异就像是生物遗传过程中的小意外,偶尔会发生。在个体的编码中,随机改变某一位的值,比如把 0 变成 1,或者把 1 变成 0 。这可以给种群引入新的基因,防止算法陷入局部最优解。就像生物界偶尔出现的基因突变,可能会诞生出更适应环境的新物种。
不断重复选择、交叉和变异的过程,种群就会像生物一样不断进化,最后得到的最优个体,很可能就是我们要找的问题的最优解。
应用场景:遗传算法的 “七十二变”
旅行商问题
之前提到的旅行商要走遍多个城市,遗传算法可以把每条可能的路线看作一个个体,通过不断进化,找到最短的路线。就好像旅行商的后代们在不断尝试各种旅行计划,最后找到了最省时间和成本的那一条。
函数优化
在数学中,我们常常要找到一个函数的最大值或最小值。遗传算法把函数的自变量编码成个体,通过进化找到使函数值最优的自变量组合。比如找到一个复杂函数在某个区间内的最大值,遗传算法就像一个聪明的探险家,在这个区间里不断探索,找到那个最高点。
机器学习中的参数调优
在训练机器学习模型时,有很多参数需要调整,不同的参数组合会影响模型的性能。遗传算法可以把这些参数组合看作个体,通过进化找到最优的参数组合,让模型的性能达到最佳。就像给模型找到最合身的 “装备”,让它在预测任务中表现得更出色。
代码实现:让代码 “进化” 起来
下面是用 C# 实现遗传算法解决旅行商问题的简单示例:
using System;
using System.Collections.Generic;
using System.Linq;
class City
{
public int X { get; set; }
public int Y { get; set; }
public City(int x, int y)
{
X = x;
Y = y;
}
}
class GeneticAlgorithm
{
private List cities;
private int populationSize;
private double mutationRate;
private int tournamentSize;
private bool elitism;
public GeneticAlgorithm(List cities, int populationSize, double mutationRate, int tournamentSize, bool elitism)
{
this.cities = cities;
this.populationSize = populationSize;
this.mutationRate = mutationRate;
this.tournamentSize = tournamentSize;
this.elitism = elitism;
}
// 生成初始种群
public List> GenerateInitialPopulation()
{
List> population = new List>();
for (int i = 0; i < populationSize; i++)
{
List route = Enumerable.Range(0, cities.Count).ToList();
route = Shuffle(route);
population.Add(route);
}
return population;
}
// 计算路线总距离
public double CalculateDistance(List route)
{
double distance = 0;
for (int i = 0; i < route.Count - 1; i++)
{
City city1 = cities[route[i]];
City city2 = cities[route[i + 1]];
distance += Math.Sqrt(Math.Pow(city2.X - city1.X, 2) + Math.Pow(city2.Y - city1.Y, 2));
}
City start = cities[route[0]];
City end = cities[route[route.Count - 1]];
distance += Math.Sqrt(Math.Pow(start.X - end.X, 2) + Math.Pow(start.Y - end.Y, 2));
return distance;
}
// 计算种群中每个个体的适应度
public List CalculateFitness(List> population)
{
List fitness = new List();
foreach (var route in population)
{
double distance = CalculateDistance(route);
fitness.Add(1 / distance);
}
return fitness;
}
// 轮盘赌选择
public List RouletteWheelSelection(List> population, List fitness)
{
double totalFitness = fitness.Sum();
double selectionPoint = new Random().NextDouble() * totalFitness;
double runningTotal = 0;
for (int i = 0; i < population.Count; i++)
{
runningTotal += fitness[i];
if (runningTotal >= selectionPoint)
{
return population[i];
}
}
return population[0];
}
// 锦标赛选择
public List TournamentSelection(List> population, List fitness)
{
List tournament = new List();
Random random = new Random();
for (int i = 0; i < tournamentSize; i++)
{
int index = random.Next(0, populationSize);
tournament.Add(index);
}
int bestIndex = tournament.OrderByDescending(i => fitness[i]).First();
return population[bestIndex];
}
// 交叉操作
public List Crossover(List parent1, List parent2)
{
int start = new Random().Next(0, parent1.Count);
int end = new Random().Next(start, parent1.Count);
List child = new List();
for (int i = 0; i < parent1.Count; i++)
{
if (i >= start && i <= end)
{
child.Add(parent1[i]);
}
else
{
foreach (int city in parent2)
{
if (!child.Contains(city))
{
child.Add(city);
break;
}
}
}
}
return child;
}
// 变异操作
public List Mutate(List route)
{
for (int i = 0; i < route.Count; i++)
{
if (new Random().NextDouble() < mutationRate)
{
int swapIndex = new Random().Next(0, route.Count);
int temp = route[i];
route[i] = route[swapIndex];
route[swapIndex] = temp;
}
}
return route;
}
// 进化种群
public List> EvolvePopulation(List> population)
{
List> newPopulation = new List>();
List fitness = CalculateFitness(population);
if (elitism)
{
int bestIndex = fitness.IndexOf(fitness.Max());
newPopulation.Add(population[bestIndex]);
}
while (newPopulation.Count < populationSize)
{
List parent1 = TournamentSelection(population, fitness);
List parent2 = TournamentSelection(population, fitness);
List child = Crossover(parent1, parent2);
child = Mutate(child);
newPopulation.Add(child);
}
return newPopulation;
}
// 打乱列表顺序
private List Shuffle(List list)
{
Random random = new Random();
for (int i = list.Count - 1; i > 0; i--)
{
int j = random.Next(0, i + 1);
int temp = list[i];
list[i] = list[j];
list[j] = temp;
}
return list;
}
}
class Program
{
static void Main()
{
List cities = new List
{
new City(0, 0),
new City(1, 1),
new City(2, 4),
new City(3, 2),
new City(4, 3)
};
GeneticAlgorithm ga = new GeneticAlgorithm(cities, 100, 0.01, 5, true);
List> population = ga.GenerateInitialPopulation();
for (int i = 0; i < 1000; i++)
{
population = ga.EvolvePopulation(population);
}
List fitness = ga.CalculateFitness(population);
int bestIndex = fitness.IndexOf(fitness.Max());
List bestRoute = population[bestIndex];
Console.WriteLine("最优路线:");
foreach (int cityIndex in bestRoute)
{
Console.Write(cityIndex + " ");
}
Console.WriteLine();
}
}
遗传算法就像一个神奇的 “进化工厂”,让代码在不断的 “繁衍” 和 “变异” 中,找到解决问题的最佳方案。要是你还想知道遗传算法在其他有趣场景中的应用,或者对代码有优化建议,欢迎随时和我聊。