mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2025-05-06 11:32:59 +00:00
150 lines
4.0 KiB
C#
150 lines
4.0 KiB
C#
using Esiur.Data;
|
|
using Esiur.Resource;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Esiur.Analysis.Optimization
|
|
{
|
|
public class Genetic<T> where T : unmanaged
|
|
{
|
|
|
|
Random rand = new Random();
|
|
|
|
public static unsafe byte[] Encode(in T value)
|
|
{
|
|
byte[] result = new byte[sizeof(T)];
|
|
fixed (byte* dst = result)
|
|
*(T*)dst = value;
|
|
return result;
|
|
}
|
|
|
|
public static unsafe T Decode(byte[] data)
|
|
{
|
|
fixed (byte* src = data)
|
|
return *(T*)src;
|
|
}
|
|
|
|
public List<T> Population = new List<T>();
|
|
|
|
public int PopulationSize { get; set; }
|
|
private int DataSize { get; set; }
|
|
|
|
public Func<T, double> FitnessFunction { get; set; }
|
|
|
|
public unsafe Genetic(int populationSize, Func<T, double> fitnessFunction)
|
|
{
|
|
FitnessFunction = fitnessFunction;
|
|
PopulationSize = populationSize;
|
|
DataSize = sizeof(T);
|
|
}
|
|
|
|
void GeneratePopultation()
|
|
{
|
|
for (var i = 0; i < PopulationSize; i++)
|
|
{
|
|
byte[] buffer = new byte[DataSize];
|
|
rand.NextBytes(buffer);
|
|
var record = Decode(buffer);
|
|
Population.Add(record);
|
|
}
|
|
}
|
|
|
|
|
|
KeyValuePair<T, double>[] GetFitness()
|
|
{
|
|
//var rt = new List<KeyValuePair<T, double>>();
|
|
var rt = new ConcurrentBag<KeyValuePair<T, double>>();
|
|
|
|
|
|
Parallel.ForEach(Population, record =>
|
|
{
|
|
rt.Add(new KeyValuePair<T, double>(record, FitnessFunction(record)));
|
|
|
|
});
|
|
|
|
//foreach (var record in Population)
|
|
// rt.Add(new KeyValuePair<T, double>( record, FitnessFunction(record)));
|
|
|
|
return rt.ToArray();
|
|
}
|
|
|
|
T Mate(T parent1, T parent2)
|
|
{
|
|
var dp1 = Encode(parent1);
|
|
var dp2 = Encode(parent2);
|
|
|
|
var dc = new byte[dp1.Length];
|
|
|
|
|
|
for (var i = 0; i < dc.Length; i++)
|
|
{
|
|
var prop = rand.NextDouble();
|
|
if (prop < 0.45)
|
|
dc[i] = dp1[i];
|
|
else if (prop < 0.9)
|
|
dc[i] = dp2[i];
|
|
else
|
|
dc[i] = (byte)rand.Next(0, 255);
|
|
}
|
|
|
|
return Decode(dc);
|
|
|
|
}
|
|
|
|
public IEnumerable<(int, double, T)> Evaluate(int maxIterations)
|
|
{
|
|
GeneratePopultation();
|
|
|
|
var generation = 0;
|
|
|
|
KeyValuePair<T, double> best;
|
|
|
|
do
|
|
{
|
|
var ordered = GetFitness().OrderBy(x => x.Value).ToArray();
|
|
|
|
best = ordered[0];
|
|
|
|
if (best.Value == 0)
|
|
break;
|
|
|
|
yield return (generation, best.Value, best.Key);
|
|
|
|
// Elitism selection ( 10% of fittest population )
|
|
|
|
var eliteCount = (int)(ordered.Length * 0.1);
|
|
var neededCount = (int)(ordered.Length * 0.9);
|
|
|
|
var newGeneration = ordered.Select(x => x.Key).Take(eliteCount).ToList();
|
|
|
|
for (var i = 0; i < neededCount; i++)
|
|
{
|
|
var p1 = Population[rand.Next(0, PopulationSize / 2)];
|
|
var p2 = Population[rand.Next(0, PopulationSize / 2)];
|
|
|
|
var offspring = Mate(p1, p2);
|
|
newGeneration.Add(offspring);
|
|
}
|
|
|
|
Population = newGeneration;
|
|
|
|
Debug.WriteLine($"Gen {generation} Fittest: {ordered.First().Value} {ordered.First().Key.ToString()} ");
|
|
|
|
|
|
} while (generation++ < maxIterations);
|
|
|
|
Debug.WriteLine($"Gen {generation} Best: {best.ToString()} ");
|
|
|
|
yield return (generation, best.Value, best.Key);
|
|
}
|
|
|
|
}
|
|
}
|