为什么 List<T>.RemoveRange(index, count) 在索引之前更改值?
Why List<T>.RemoveRange(index, count) changes value before index?
我正在实现一个经典的遗传算法。在交叉阶段,我发现了一些奇怪的行为。
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
,我们首先使用 RemoveRange
从 Genes[8]
删除基因到 Genes[11]
,然后我们使用 RemoveRange
添加来自另一个染色体的基因 AddRange
.
有时会发生一些奇怪的事情:当我们使用 RemoveRange
时,Genes[7]
(对于本例)将其值从 0 更改为 1 或从 1 更改为 0。它不会在每次迭代中发生,有时一切正常。我注意到 locus = 7..11
.
更常发生这种情况
它不会对算法造成太大伤害(只是更多的突变 :D)。但是有人知道为什么它会反转值吗?
更新:
非常感谢 BJ Myers 的无懈可击的回答。
稍后阅读此 post 的其他人可能会对为什么会这样感兴趣。解释的不错here.
RemoveRange
未更改指定索引之前的值。看起来是这样的原因是因为你的索引差了一个。
看看这一行:
pair.First.Chromosomes[i].Genes.RemoveRange(locus - 1, crossoverLength);
如果我们假设(如您的示例)locus = 8
,因此假设 crossoverLength = 4
,则所需的行为是删除索引为 [8]
到 [11]
的元素。但是,由于您从 locus
中减去一个,因此您将 7
作为第一个参数传递给 RemoveRange
,因此从 [10]
中删除了元素 [7]
。
对于 RemoveRange
:
的任一调用,正确的代码不应包含 - 1
偏移量
pair.First.Chromosomes[i].Genes.RemoveRange(locus, crossoverLength);
您认为元素 [7]
发生变化的行为实际上是元素 [7]
到 [10]
被删除以及之前位于 [11]
的元素向下移动的结果进入位置 [7]
。如果您的 "genes" 始终是二进制的,那么作为 RemoveRange
调用的结果,该值有 50/50 的可能性 "change"。
我正在实现一个经典的遗传算法。在交叉阶段,我发现了一些奇怪的行为。
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
,我们首先使用 RemoveRange
从 Genes[8]
删除基因到 Genes[11]
,然后我们使用 RemoveRange
添加来自另一个染色体的基因 AddRange
.
有时会发生一些奇怪的事情:当我们使用 RemoveRange
时,Genes[7]
(对于本例)将其值从 0 更改为 1 或从 1 更改为 0。它不会在每次迭代中发生,有时一切正常。我注意到 locus = 7..11
.
它不会对算法造成太大伤害(只是更多的突变 :D)。但是有人知道为什么它会反转值吗?
更新:
非常感谢 BJ Myers 的无懈可击的回答。 稍后阅读此 post 的其他人可能会对为什么会这样感兴趣。解释的不错here.
RemoveRange
未更改指定索引之前的值。看起来是这样的原因是因为你的索引差了一个。
看看这一行:
pair.First.Chromosomes[i].Genes.RemoveRange(locus - 1, crossoverLength);
如果我们假设(如您的示例)locus = 8
,因此假设 crossoverLength = 4
,则所需的行为是删除索引为 [8]
到 [11]
的元素。但是,由于您从 locus
中减去一个,因此您将 7
作为第一个参数传递给 RemoveRange
,因此从 [10]
中删除了元素 [7]
。
对于 RemoveRange
:
- 1
偏移量
pair.First.Chromosomes[i].Genes.RemoveRange(locus, crossoverLength);
您认为元素 [7]
发生变化的行为实际上是元素 [7]
到 [10]
被删除以及之前位于 [11]
的元素向下移动的结果进入位置 [7]
。如果您的 "genes" 始终是二进制的,那么作为 RemoveRange
调用的结果,该值有 50/50 的可能性 "change"。