笔者最近研究生课题研究一点与优化相关的内容,调研遗传算法觉得很合适(遗传算法此处不过多介绍),但网上都是matlab上面的实现方法,虽然很简单,但我要用在c#系统上面,就打算自己写一个,中间遇到了很多坑,千言万语汇成一句:写算法千万别用c#。
话不多说直接上代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace GAtest
{
public class GA
{
//定义种群大小、最大迭代次数、交叉概率和变异概率
private int populationSize = 30;
private int maxIterations = 100;
private double crossoverProbability = 0.7;
private double mutationProbability = 0.1;
private int inputSize = 1;//要优化的参数个数
//定义优化函数
private double Evaluate(double x)
{
return x+10*Math.Sin(5*x)+ 7 * Math.Cos(4 * x);
}
//定义个体类
class Individual
{
public double[] genes;//参数列表,此处例子为单参数优化
public double fitness;//适应度值
public List<int[]> genesCoding;//基因编码
public Individual(double[] genes, double fitness, List<int[]> genesCoding)
{
this.genes = genes;
this.fitness = fitness;
this.genesCoding = genesCoding;
}
}
//生成初始种群
private Individual[] InitializePopulation()
{
Random rand = new Random(GetRandomSeed());
Individual[] population = new Individual[populationSize];
for (int i = 0; i < populationSize; i++)
{
double[] genes = new double[inputSize];
//确定每个值的范围
genes[0] = rand.NextDouble() * 20 - 10; //随机生成[-10,10)之间的实数
//genes[1] = rand.NextDouble() * 4 + 1; //随机生成[1,5)之间的实数
List<int[]> genesCoding = GenesCoding(genes);//将值转化成编码
double fitness = Evaluate(genes[0]);
population[i] = new Individual(genes, fitness, genesCoding);
}
return population;
}
//编码
public List<int[]> GenesCoding(double[] individual)
{
List<int[]> genesCoding = new List<int[]>();
//--------第一个参数----------范围是[-10,10]
int value = (int)((individual[0] + 10) / (20 / (Math.Pow(2, 20) - 1)));
int[] binaryArray = new int[20]; // 用于存储二进制数组
int j = binaryArray.Length - 1; // 数组下标从右向左开始
while (value > 0 && j >= 0)
{
binaryArray[binaryArray.Length - 1-j] = value % 2;
value /= 2;
j--;
}
genesCoding.Add(binaryArray);
--------第二个参数----------范围是[1,5]
//value = (int)((individual[1] - 1) / (4 / (Math.Pow(2, 10) - 1)));
//binaryArray = new int[10]; // 用于存储二进制数组
//j = binaryArray.Length - 1; // 数组下标从右向左开始
//while (value > 0 && j >= 0)
//{
// binaryArray[j--] = value % 2;
// value /= 2;
//}
//genesCoding.Add(binaryArray);
return genesCoding;
}
private List<int[]> GenesCoding1(Individual individual)
{
List<int[]> genesCoding = new List<int[]>();
//genesCoding.Add()
return genesCoding;
}
//解码
public double[] GenesEnconding(List<int[]> genesCoding)
{
double[] genes = new double[genesCoding.Count];
//--------第一个参数----------范围是[-10,10]
int value = 0;
for (int i = 0; i < genesCoding[0].Length; i++)
{
value += genesCoding[0][i] * (int)Math.Pow(2, i);
}
genes[0] = -10 + value * 20 / (Math.Pow(2, 20) - 1);
//--------第一个参数----------范围是[1,5]
//value = 0;
//for (int i = 0; i < genesCoding[1].Length; i++)
//{
// value += genesCoding[1][genesCoding[0].Length - i - 1] * (int)Math.Pow(2, i);
//}
//genes[1] = 1 + value * 5 / (Math.Pow(2, 10) - 1);
return genes;
}
//选择操作
private Individual Selection(Individual[] population)
{
Individual[] input = population;
double sumFitness = 0;
//计算种群适应度之和
for (int i = 0; i < populationSize; i++)
{
sumFitness = sumFitness + input[i].fitness;
}
Random rand = new Random(GetRandomSeed());
//轮盘赌选择
double r = rand.NextDouble() * sumFitness; //生成0,1的双精度浮点数
double tmpSum = 0;
for (int i = 0; i < populationSize; i++)
{
tmpSum = tmpSum + input[i].fitness;
if (tmpSum >= r)
{
Individual output = new Individual(input[i].genes,input[i].fitness, input[i].genesCoding);
return output;
}
}
Individual output1 = new Individual(input[3].genes, input[3].fitness, input[3].genesCoding);
return output1;
}
//交叉操作
private Individual[] Crossover(Individual parent1, Individual parent2)
{
Individual[] result = new Individual[2];
result[0] = new Individual(parent1.genes, parent1.fitness, parent1.genesCoding);
result[1] = new Individual(parent2.genes, parent2.fitness, parent2.genesCoding);
Random rand = new Random(GetRandomSeed());
if (rand.NextDouble() > crossoverProbability)
{
return result;
}
for (int j = 0; j < parent1.genes.Length; j++)
{
rand = new Random(GetRandomSeed());
int geneLength = parent1.genesCoding[j].Length;
//随机生成交叉点
int crossoverPoint = rand.Next(geneLength);
rand = new Random(GetRandomSeed());
//随机生成交叉长度
int crossLenth = rand.Next(geneLength - crossoverPoint);
//交叉
for (int i = crossoverPoint; i < crossoverPoint + crossLenth; i++)
{
result[0].genesCoding[j][i] = parent2.genesCoding[j][i];
result[1].genesCoding[j][i] = parent1.genesCoding[j][i];
}
}
result[0].genes = GenesEnconding(result[0].genesCoding);
result[1].genes = GenesEnconding(result[1].genesCoding);
return result;
}
//变异操作,单点变异
private Individual Mutation(Individual input)
{
Random rand = new Random(GetRandomSeed());
double b = rand.NextDouble();
Individual output = new Individual(input.genes, input.fitness, input.genesCoding);
if (b > mutationProbability)
{
return output;
}
for (int j = 0; j < output.genes.Length; j++)
{
rand = new Random(GetRandomSeed());
int geneLength = output.genesCoding[j].Length;
//随机生成变异点
int mutationPoint = rand.Next(geneLength);
//变异
output.genesCoding[j][mutationPoint] = output.genesCoding[j][mutationPoint] == 0 ? 1 : 0;
}
output.genes = GenesEnconding(output.genesCoding);
return output;
}
//遗传算法主体
public void Run()
{
Individual[] population = InitializePopulation();
int iteration = 0;
double[] sumCount = new double[maxIterations];
while (iteration < maxIterations)
{
Individual[] newPopulation = new Individual[populationSize];
double sum = 0;
int maxIndex = 0;//最优值的序号
double maxValue = 0;//最优值所对应的适应度值
for(int i = 0; i < populationSize; i++)
{
if(population[i].fitness>maxValue)
{
maxValue = population[i].fitness;
maxIndex = i;
}
sum += population[i].fitness;
}
sumCount[iteration] = sum/ populationSize;//平均适应度
//生成新种群
for (int i = 0; i < populationSize; i++)
{
if (i == maxIndex)//精英策略,每一代最优的个体留下来
{
newPopulation[i] = population[i];
continue;
}
//随机选择父母
Individual[] newPop = (Individual[])population.Clone();
Individual parent1 = Selection(newPop);
Individual parent2 = Selection(newPop);
//交叉
Individual[] children = Crossover(parent1,parent2);
//变异
children[0] = Mutation(children[0]);
children[1] = Mutation(children[1]);
children[0].fitness = Evaluate(children[0].genes[0]) ;//计算适应度
children[1].fitness = Evaluate(children[1].genes[0]);//计算适应度
newPopulation[i] = children[0].fitness > children[1].fitness ? children[0] : children[1];//选择最优的交叉变异个体
}
population = (Individual[])newPopulation.Clone();
iteration++;
Console.WriteLine("迭代第{0}代,平均适应度为{1}", iteration, sumCount[iteration]);
}
//输出最优解
double bestFitness = double.MinValue;
double bestGene1 = 0;
//double bestGene2 = 0;
for (int i = 0; i < populationSize; i++)
{
if (population[i].fitness > bestFitness)
{
bestFitness = population[i].fitness;
bestGene1 = population[i].genes[0];
//bestGene2 = population[i].genes[1];
}
}
Console.WriteLine("最优解:x = {0},f(x) = {1}", bestGene1, bestFitness);
}
public static int GetRandomSeed()//随机种子,防止短时间内生成的随机数相同
{
byte[] bytes = new byte[4];
System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
rng.GetBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
}
}
直接运行run()函数即可实现,对环境没有任何要求。
要是多优化问题就需要在初始化个体种群时给genes赋多个值,还需要修改编码函数和解码函数,确定不同的取值范围等。