2016-10-23 2 views
0

Я реализую классический генетический алгоритм. В фазе кроссовера я нахожу странное поведение.Почему List <T> .RemoveRange (индекс, счет) изменяет значение перед индексом?

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, затем добавляем гены из другой хромосомы с использованием AddRange.

Иногда случается что-то странное: когда мы используем RemoveRange, Genes[7] (для этого экземпляра) меняет свое значение от 0 до 1 или от 1 до 0. Это не происходит на каждой итерации, иногда все работает нормально. Я заметил, что это происходит чаще всего для locus = 7..11.

Это не слишком сильно вредит алгоритму (просто больше мутаций: D). Но кто-нибудь знает, почему он инвертирует ценности?

Strange behaviour proof

Update:

Большого спасибо BJ Myers для исчерпывающего ответа. Все, кто прочитает это сообщение позже, могут быть заинтересованы, почему это происходит. Это объясняется хорошим here.

ответ

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]. Если ваши «гены» всегда являются двоичными, тогда существует вероятность 50/50, что значение «изменится» в результате вызова RemoveRange.

Смежные вопросы