我正在C#WinForms中实现遗传算法,以找到一个非常简单的数学方程式的最优解。这里的“简单”一词意味着方程式必须具有以下性质:
有人认为,方程非常简单,我就能达到最佳解。但是,我总是只能得到非常接近最佳解决方案的解决方案。为了评估解决方案是否最佳,我使用以下公式:
f(x) = absolute((sum_of_all_variables) - equation_result)
例如,我有以下等式:
1a + 1b = 12
如果a
是2
,而b
是3
,则该个人的f(x)
将是abs(2 + 3 - 12) = 7
。当f(x)
达到0
或生成了50代时,我的代码停止了。
我当前的突变率是50%,我的交叉率是25%。使用的选择方法是轮盘赌轮选择。突变方法是随机突变,即我只是从基因库中选择一个随机基因来更改其值,每代最多可以更改50%的基因。
我的期望:该代码产生的解决方案(单个)的f(x)
值为0,这是可用的最佳解决方案之一。
当前结果:代码产生的解决方案几乎是最佳的。 我发现的一个常见模式是,几乎是最优的解决方案通常会在下一代中完全复制。
我现在对这个问题的猜测:
我认为这与我的跨界车风格有关。我要做的交叉:
但是,正如我所说,它不能产生最佳结果。我的逻辑有问题吗?或者这是遗传算法在这个简单数学方程式上的预期行为?
我用来参考的交叉方法(我正在使用C#WinForms在ListView中显示数据)
private static void Crossover(List<Chromosome> chromosomes, Random seed)
{
List<int> crossoverChromosome = new List<int>();
for (int i = 0; i < 50; i++)
{
decimal randomedValue = RandomizeValue(seed);
if (randomedValue < Population.CrossoverRate) crossoverChromosome.Add(i);
}
for (int i = 0; i < crossoverChromosome.Count; i++)
{
int crossoverPoint = seed.Next(0, chromosomes[0].GeneValues.Count);
chromosomes[crossoverChromosome[i]] = chromosomes[crossoverChromosome[i]].MixChromosome(chromosomes[crossoverChromosome[(i + 1) % crossoverChromosome.Count]], crossoverPoint);
}
}
private static decimal RandomizeValue(Random seed)
{
return Math.Round((decimal)seed.NextDouble(), 5);
}
public Chromosome MixChromosome(Chromosome mixture, int crossoverPoint)
{
List<Gene> newGenes = new List<Gene>();
newGenes.AddRange(this.GetGenes(0, crossoverPoint));
newGenes.AddRange(mixture.GetGenes(crossoverPoint, this.GeneValues.Count));
return new Chromosome(DesiredValue, OperatorData, newGenes); // Ignore the DesiredValue and Operator Data, it has nothing to do with crossover
}
private List<Gene> GetGenes(int firstIndex, int lastIndex)
{
List<Gene> slicedGenes = new List<Gene>();
for (int i = firstIndex; i < lastIndex; i++)
{
slicedGenes.Add(Genes[i].CloneGene());
}
return slicedGenes;
}
我只测试了带有两个变量的方程式。
编辑
其他信息:
对方程1a + 1b = 10
进行10次重试后,经过50代,得出以下最佳适应度值:
a
4和值b
7。答案 0 :(得分:2)
一个市长的问题是(就像您假设的那样)分频器。用您描述它的方式(或至少据我了解),您对随机选择以生成下一个总体的解决方案的每个人进行交叉。
遗传算法背后的基本思想是达尔文的适者生存,而不是某些随机生存(或繁殖)。
所以我认为问题在于每个人都有相同的繁殖机会,这在遗传算法中意义不大。更好的解决方案是让导致结果接近正确结果的个体比不能带来良好结果的个体繁殖更多的东西。否则,它会与随机搜索非常相似。
通常情况下,随机选择要复制的个体仍然有用,因为这将导致结果并不总是最好的,但会探索更大范围的寻找区域。>
执行此操作的一种常见方法是使用fitness-proportional selection,它将根据适应度值随机选择要复制的个体。因此,适应度高的个体(导致结果接近您示例中正确的个体)繁殖的可能性更高。
另一种常见的方式是stochastically distributed selection,它还会选择随机的个体进行繁殖,并且有更高的机会获得更好的个体,但是这也可以确保优于平均适应度至少会重复一次。。
健身-比例选择的示例实现可能如下所示(不幸的是,它是Java代码,没有C#,但它们非常相似...):
import java.util.concurrent.ThreadLocalRandom;
import com.google.common.annotations.VisibleForTesting;
/**
* A selector that randomly chooses pairs to be selected for reproduction based on their probability to be selected.
*/
public class FitnessProportionalSelector implements Selector {
/**
* Select pairs of parents (by index) that are combined to build the next generation.
*
* @param selectionProbability
* The probability to be selected for every DNA in the current population (sums up to 1).
*
* @param numPairs
* The number of pairs needed (or the number of individuals needed in the next generation).
*
* @return Returns an int-array of size [numPairs * 2] including the pairs that are to be combined to create the next population (a pair is on
* position [i, i+1] for i % 2 = 0).
*/
@Override
public int[] select(double[] selectionProbability, int numPairs) {
double[] summedProbabilities = Selector.toSummedProbabilities(selectionProbability);
int[] selectionPairs = new int[numPairs * 2];
double chosenProbability;
for (int i = 0; i < numPairs * 2; i++) {
chosenProbability = getRandomNumber();
selectionPairs[i] = Selector.getSelectedIndexByBisectionSearch(summedProbabilities, chosenProbability);
}
return selectionPairs;
}
@Override
public String toString() {
return "FitnessProportionalSelector []";
}
@VisibleForTesting
/*private*/ double getRandomNumber() {
return ThreadLocalRandom.current().nextDouble();
}
}
或随机分布选择的解决方案(也在Java中):
import java.util.concurrent.ThreadLocalRandom;
import com.google.common.annotations.VisibleForTesting;
/**
* A selector that chooses the pairs to be reproduced by a stochastically distributed selection method.
*
* The selection probability is proportional to the given probability, but it's ensured, that individuals with a probability above average are chosen
* at least once.
*/
public class StochasticallyDistributedSelector implements Selector {
/**
* Select pairs of parents (by index) that are combined to build the next generation.
*
* @param selectionProbability
* The probability to be selected for every DNA in the current population (sums up to 1).
*
* @param numPairs
* The number of pairs needed (or the number of individuals needed in the next generation).
*
* @return Returns an int-array of size [numPairs * 2] including the pairs that are to be combined to create the next population (a pair is on
* position [i, i+1] for i % 2 = 0).
*/
@Override
public int[] select(double[] selectionProbability, int numPairs) {
double[] summedProbability = Selector.toSummedProbabilities(selectionProbability);
int[] selectedPairs = new int[2 * numPairs];
double startPoint = getRandomNumber();
double addedAverage = 1d / (2d * numPairs);
double stochasticallySelectedProbability;
for (int i = 0; i < numPairs * 2; i++) {
//select the pairs stochastically
stochasticallySelectedProbability = startPoint + i * addedAverage;
stochasticallySelectedProbability %= 1;
selectedPairs[i] = Selector.getSelectedIndexByBisectionSearch(summedProbability, stochasticallySelectedProbability);
}
//shuffle the pairs to distribute them stochastically
shuffle(selectedPairs);
return selectedPairs;
}
@VisibleForTesting
/*private*/ void shuffle(int[] selectedPairs) {
//shuffle the selected pairs in place
int swapIndex;
int tmp;
for (int i = selectedPairs.length - 1; i > 0; i--) {
swapIndex = (int) (getRandomNumber() * (i + 1));
tmp = selectedPairs[i];
selectedPairs[i] = selectedPairs[swapIndex];
selectedPairs[swapIndex] = tmp;
}
}
@VisibleForTesting
/*private*/ double getRandomNumber() {
return ThreadLocalRandom.current().nextDouble();
}
@Override
public String toString() {
return "StochasticallyDistributedSelector []";
}
}
我从为硕士论文创建的遗传优化器项目中提取了这些示例代码。如果您想看看它,可以找到它on my github account
一些进一步的改进: