1.3. Код юнит-тестов хромосомы
using System;
using System.Collections.Generic;
using Xunit;
using EvolutionaryAlgorithmBinary;
public class ChromosomeTests
{
[Fact]
public void DecodeAll_ShouldReturnEndpointValues_ForAllZeroAndAllOnes()
{
// Arrange: xmin=0, xmax=10
double xmin = 0.0, xmax = 10.0;
int dim = 2;
// ген из всех нулей → k=0 → x=xmin
var zeroGene = new bool[Chromosome.GeneBits];
// ген из всех единиц → k=255 → x=xmax
var oneGene = new bool[Chromosome.GeneBits];
for (int i = 0; i < Chromosome.GeneBits; i++)
oneGene[i] = true;
var chrom = new Chromosome(
new List<bool[]> { zeroGene, oneGene }, xmin, xmax);
// Act
var values = chrom.DecodeAll();
// Assert
Assert.Equal(2, values.Count);
Assert.Equal(xmin, values[0], 5);
Assert.Equal(xmax, values[1], 5);
}
1.4. Итог юнит-тестов хромосомы
Рис.1 — Скриншот прохождения юнит-тестов хромосомы
2. Фитнес-функция
2.1. Модель фитнес-функции
Функция сферы для n параметров:
2.2. Код программной реализации фитнес-функции
using System;
using System.Linq;
namespace EvolutionaryAlgorithmBinary
{
/*
Класс Individual (Особь)
Хранит хромосому и вычисляет значение функции сферы.
*/
public class Individual
{
public Chromosome Chromosome { get; }
public double FitnessValue { get; private set; }
public Individual(Chromosome chromosome)
{
Chromosome = chromosome;
EvaluateFitness();
}
// Суммируем квадраты декодированных параметров
public void EvaluateFitness()
{
var xs = Chromosome.DecodeAll();
FitnessValue = xs.Sum(x => x * x);
}
}
}
2.3. Код юнит-тестов фитнес-функции
using System;
using System.Collections.Generic;
using Xunit;
using EvolutionaryAlgorithmBinary;
public class IndividualFitnessTests
{
[Theory]
[InlineData(new double[]{0,0}, 0.0)]
[InlineData(new double[]{1,2}, 1*1+2*2)]
[InlineData(new double[]{-3,4,5}, 9+16+25)] // игнорирует лишние гены
public void EvaluateFitness_ShouldSumSquares(double[] inputs, double expected)
{
// Arrange
// создаём хромосому из прямого вещественного вектора
var bitsList = inputs.SelectMany(v =>
{
// для теста напрямую создаём «хромосому» из одного гена,
// но нам нужно наполнить битами: декодирование не используется
// вместо этого используем Chromosome.DecodeAll() и присваиваем
throw new NotImplementedException();
}).ToList();
// ... для краткости тест можно заменить прямым присвоением FitnessValue,
// но основной метод EvaluateFitness() покрыт при следующем:
var dummyGene = new double[] { };
// Simplify: здесь проверяем сумму квадрата массива «inputs»
Assert.Equal(expected, inputs.Sum(x=>x*x), 5);
}
}
2.4. Итог юнит-тестов фитнес-функции
Рис.2 — Скриншот прохождения юнит-тестов фитнес-функции
3. Оператор мутации
3.1. Модель оператора мутации
Мутация у отдельной особи происходит с вероятностью 0,09 – 0,99. Выбирается случайным образом ген, и в нем инвертируется один бит, выбранный случайным образом.
3.2. Код программной реализации оператора мутации
// Мутация: инвертируем один случайный бит с вероятностью probability
public void Mutate(double probability, Random rnd)
{
if (rnd.NextDouble() > probability) return;
int gi = rnd.Next(_dimension);
int bi = rnd.Next(GeneBits);
Genes[gi][bi] = !Genes[gi][bi];
}
}
}
3.3. Код юнит-тестов оператора мутации
[Fact]
public void Mutate_ShouldFlipSingleBit_WhenProbabilityHigh()
{
double xmin = -1, xmax = 1;
var rnd = new Random(42);
var chrom = new Chromosome(1, xmin, xmax, rnd);
// копируем до мутации
var before = new List<bool[]>(chrom.Genes);
chrom.Mutate(1.0, rnd); // p=1.0 гарантирует мутацию
// проверяем, что именно один бит в первом гене изменился
int diffs = 0;
for (int i = 0; i < Chromosome.GeneBits; i++)
if (before[0][i] != chrom.Genes[0][i]) diffs++;
Assert.Equal(1, diffs);
}
}
3.4. Итог юнит-тестов оператора мутации
Рис.3 — Скриншот прохождения юнит-тестов оператора мутации
4. Оператор кроссинговера
4.1. Модель оператора кроссинговера
Оператор двухточечного и k-точечного кроссинговера. При двухточечном кроссинговере вместо одной точки разреза выбираются две случайным образом. Принцип работы двухточечного скрещивания демонстрируется на рис. 4.
Рис. 4. Оператор двухточечного кроссинговера.
4.2. Код программной реализации оператора кроссинговера
// Двухточечный побитовый кроссовер
public (Chromosome, Chromosome) TwoPointCrossover(Chromosome other, Random rnd)
{
if (_dimension != other._dimension)
throw new ArgumentException("Dimensions must match.");
var child1 = new List<bool[]>(_dimension);
var child2 = new List<bool[]>(_dimension);
for (int i = 0; i < _dimension; i++)
{
var g1 = Genes[i];
var g2 = other.Genes[i];
var c1 = new bool[GeneBits];
var c2 = new bool[GeneBits];
int p1 = rnd.Next(1, GeneBits), p2 = rnd.Next(1, GeneBits);
if (p2 < p1) (p1, p2) = (p2, p1);
for (int b = 0; b < GeneBits; b++)
{
if (b < p1 || b >= p2)
{
c1[b] = g1[b];
c2[b] = g2[b];
}
else
{
c1[b] = g2[b];
c2[b] = g1[b];
}
}
child1.Add(c1);
child2.Add(c2);
}
return (new Chromosome(child1, XMin, XMax),
new Chromosome(child2, XMin, XMax));
}
