为什么List <t> .RemoveRange(index,count)在索引之前更改值?

时间:2016-10-23 17:01:58

标签: c# .net list ienumerable genetic-algorithm

我正在实施一种经典的遗传算法。在交叉阶段,我发现了一些奇怪的行为。

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 = 8crossoverLength = 4,我们首先使用Genes[8]将基因从Genes[11]移除到RemoveRange,然后我们使用{添加来自另一条染色体的基因{1}}。

有时会发生奇怪的事情:当我们使用AddRange时,RemoveRange(对于此实例)将其值从0更改为1或从1更改为0.每次迭代都不会发生这种情况,有时会发生一切正常。我注意到Genes[7]更频繁地发生这种情况。

它不会对算法造成太大的伤害(只有更多的突变:D)。但有人知道为什么它会反转价值吗?

Strange behaviour proof

更新

非常感谢BJ Myers的无可辩驳的回答。 其他人,以后会阅读这篇文章,可能会感兴趣,为什么会这样。它解释得很好here

1 个答案:

答案 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电话的结果。