我正在实施一种经典的遗传算法。在交叉阶段,我发现了一些奇怪的行为。
private static void Crossover(ref List<CrossoverPair> pairs)
{
var random = new Random();
//TODO debug this
foreach (var pair in pairs)
{
for (var i = 0; i < pair.First.Chromosomes.Count; i++)
{
var locus = random.Next(1, 12);
var crossoverLength = pair.First.Chromosomes[i].Genes.Count - locus;
var swapFirst = pair.First.Chromosomes[i].Genes.Skip(locus).Take(crossoverLength).ToList();
var swapSecond = pair.Second.Chromosomes[i].Genes.Skip(locus).Take(crossoverLength).ToList();
pair.First.Chromosomes[i].Genes.RemoveRange(locus - 1, crossoverLength);
pair.First.Chromosomes[i].Genes.AddRange(swapSecond);
pair.Second.Chromosomes[i].Genes.RemoveRange(locus - 1, crossoverLength);
pair.Second.Chromosomes[i].Genes.AddRange(swapFirst);
}
}
}
每条染色体含有12个基因。它从随机定义的基因座开始交换同源部分。例如,如果我们有locus = 8
和crossoverLength = 4
,我们首先使用Genes[8]
将基因从Genes[11]
移除到RemoveRange
,然后我们使用{添加来自另一条染色体的基因{1}}。
有时会发生奇怪的事情:当我们使用AddRange
时,RemoveRange
(对于此实例)将其值从0更改为1或从1更改为0.每次迭代都不会发生这种情况,有时会发生一切正常。我注意到Genes[7]
更频繁地发生这种情况。
它不会对算法造成太大的伤害(只有更多的突变:D)。但有人知道为什么它会反转价值吗?
更新
非常感谢BJ Myers的无可辩驳的回答。 其他人,以后会阅读这篇文章,可能会感兴趣,为什么会这样。它解释得很好here。
答案 0 :(得分:2)
RemoveRange
未更改指定索引之前的值。它看起来的原因是因为你的索引是一个。
看看这一行:
pair.First.Chromosomes[i].Genes.RemoveRange(locus - 1, crossoverLength);
如果我们假设(如您的示例中)locus = 8
因此crossoverLength = 4
,则所需的行为是删除索引为[8]
到[11]
的元素。但是,由于您已从locus
中减去一个,因此您将7
作为RemoveRange
的第一个参数传递,因此将[7]
元素移除到[10]
。
对于- 1
的任一调用,正确的代码不应包含RemoveRange
偏移量:
pair.First.Chromosomes[i].Genes.RemoveRange(locus, crossoverLength);
您认为元素[7]
发生变化的行为实际上是元素[7]
到[10]
被移除的结果,以前元素[11]
移位到位置[7]
。如果你的基因&#34;总是二元的,然后有50/50的机会,价值会变化&#34; RemoveRange
电话的结果。