-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
ojanacek
committed
Jan 13, 2016
1 parent
6ddf069
commit 3c3bd7f
Showing
8 changed files
with
406 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System.Collections; | ||
using System.Linq; | ||
|
||
namespace KnapsackProblem.SimulatedEvolution | ||
{ | ||
public sealed class Chromosome | ||
{ | ||
public BitArray Gens { get; set; } | ||
|
||
public int Weight { get; set; } | ||
public int Fitness { get; set; } | ||
|
||
public Chromosome(BitArray gens) | ||
{ | ||
Gens = gens; | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return $"Weight: {Weight}, Fitness: {Fitness}, Gens: {string.Join(" ", Gens.Cast<bool>().Select(b => b ? "1" : "0"))}"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using KnapsackProblem.Common; | ||
|
||
namespace KnapsackProblem.SimulatedEvolution | ||
{ | ||
public sealed class GeneticAlgorithm | ||
{ | ||
private static readonly Random random = new Random(); | ||
private readonly GeneticAlgorithmArgs args; | ||
private int chromosomeLength; | ||
|
||
public GeneticAlgorithm(GeneticAlgorithmArgs args) | ||
{ | ||
this.args = args; | ||
} | ||
|
||
public KnapsackSolution Solve(Knapsack knapsack) | ||
{ | ||
chromosomeLength = knapsack.InstanceSize; | ||
var population = CreatePopulation(args.PopulationSize); | ||
int generation = 0; | ||
|
||
while (true) | ||
{ | ||
foreach (var chromosome in population) | ||
{ | ||
EvaluateFitness(chromosome, knapsack); | ||
} | ||
|
||
PrintStatus(generation, population); | ||
if (generation == args.MaxGenerations) // won't trigger if max generations is not set | ||
break; | ||
|
||
var newPopulation = new Chromosome[args.PopulationSize]; | ||
var breedingPool = new List<Chromosome>(args.PopulationSize); | ||
int takeElites = 0; | ||
if (args.ElitismDegree > 0) | ||
{ | ||
takeElites = (int) (args.PopulationSize * args.ElitismDegree); | ||
var elites = population.OrderByDescending(ch => ch.Fitness).Take(takeElites).ToArray(); | ||
Array.Copy(elites, newPopulation, elites.Length); | ||
} | ||
|
||
for (int i = takeElites; i < args.PopulationSize; i++) | ||
{ | ||
breedingPool.Add(population.TakeRandom(args.TournamentSize, random).OrderByDescending(ch => ch.Fitness).First()); | ||
} | ||
|
||
for (int i = takeElites; i < args.PopulationSize - 1; i++) | ||
{ | ||
newPopulation[i] = Cross(breedingPool[i - takeElites], breedingPool[i + 1 - takeElites]); | ||
} | ||
newPopulation[args.PopulationSize - 1] = Cross(breedingPool[args.PopulationSize - 1 - takeElites], breedingPool[0]); | ||
MutateRandomChromosome(newPopulation); // TODO: could be parametrized | ||
|
||
population = newPopulation; | ||
generation++; | ||
} | ||
|
||
var fittest = population.OrderBy(ch => ch.Fitness).First(); | ||
return new KnapsackSolution(fittest.Fitness, fittest.Gens, knapsack); | ||
} | ||
|
||
private Chromosome[] CreatePopulation(int populationSize) | ||
{ | ||
var population = new Chromosome[populationSize]; | ||
var possibleChromosomesCount = (int)Math.Pow(2, chromosomeLength); | ||
for (int i = 0; i < populationSize; i++) | ||
{ | ||
int randomNumber = random.Next(1, possibleChromosomesCount); | ||
var gens = new BitArray(chromosomeLength); | ||
for (int j = 0; j < chromosomeLength; j++) | ||
{ | ||
if ((randomNumber & (1 << j)) != 0) | ||
gens.Set(j, true); | ||
} | ||
population[i] = new Chromosome(gens); | ||
} | ||
return population; | ||
} | ||
|
||
public static void EvaluateFitness(Chromosome chromosome, Knapsack knapsack) | ||
{ | ||
for (int i = 0; i < chromosome.Gens.Count; i++) | ||
{ | ||
if (chromosome.Gens.Get(i)) | ||
{ | ||
var matchingItem = knapsack.Items[i]; | ||
chromosome.Weight += matchingItem.Weight; | ||
chromosome.Fitness += matchingItem.Price; | ||
} | ||
|
||
if (chromosome.Weight > knapsack.Capacity) | ||
chromosome.Fitness = 0; | ||
} | ||
} | ||
|
||
private static Chromosome Cross(Chromosome chromosome, Chromosome other) | ||
{ | ||
// TODO: consider creating two offsprings instead of one | ||
var gensCount = chromosome.Gens.Length; | ||
int randomCutIndex = random.Next(1, gensCount); | ||
var newGens = new BitArray(chromosome.Gens); | ||
for (int i = randomCutIndex; i < gensCount; i++) | ||
{ | ||
newGens.Set(i, other.Gens.Get(i)); | ||
} | ||
return new Chromosome(newGens); | ||
} | ||
|
||
private void MutateRandomChromosome(Chromosome[] population) | ||
{ | ||
int randomChromosomeIndex = random.Next(0, population.Length); | ||
int randomGeneIndex = random.Next(0, chromosomeLength); | ||
population[randomChromosomeIndex].Gens[randomGeneIndex] = !population[randomChromosomeIndex].Gens[randomGeneIndex]; | ||
} | ||
|
||
private static void PrintStatus(int generation, Chromosome[] population) | ||
{ | ||
int maxFitness = population.Max(ch => ch.Fitness); | ||
int totalFitness = population.Sum(ch => ch.Fitness); | ||
Console.WriteLine($"Generation {generation.PadRight(2)}: max fitness - {maxFitness.PadRight(8)}, total fitness - {totalFitness.PadRight(8)}"); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
KnapsackProblem.SimulatedEvolution/GeneticAlgorithmArgs.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace KnapsackProblem.SimulatedEvolution | ||
{ | ||
public sealed class GeneticAlgorithmArgs | ||
{ | ||
public int PopulationSize { get; } | ||
public int MaxGenerations { get; } | ||
public int TournamentSize { get; } | ||
public double ElitismDegree { get; } | ||
|
||
public GeneticAlgorithmArgs(int populationSize, int maxGenerations, int tournamentSize, double elitismDegree) | ||
{ | ||
PopulationSize = populationSize; | ||
MaxGenerations = maxGenerations; | ||
TournamentSize = tournamentSize; | ||
ElitismDegree = elitismDegree; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace KnapsackProblem.SimulatedEvolution | ||
{ | ||
public static class Helpers | ||
{ | ||
public static IEnumerable<T> TakeRandom<T>(this T[] array, int count, Random random) | ||
{ | ||
for (int i = 0; i < count; i++) | ||
{ | ||
int randomIndex = random.Next(0, array.Length); | ||
yield return array[randomIndex]; | ||
} | ||
} | ||
|
||
public static string PadRight(this int number, int totalWidth) | ||
{ | ||
return number.ToString().PadLeft(totalWidth, ' '); | ||
} | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
KnapsackProblem.SimulatedEvolution/KnapsackProblem.SimulatedEvolution.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProjectGuid>{866D9121-78DE-4D3B-8D77-6596A11BC5C0}</ProjectGuid> | ||
<OutputType>Exe</OutputType> | ||
<AppDesignerFolder>Properties</AppDesignerFolder> | ||
<RootNamespace>KnapsackProblem.SimulatedEvolution</RootNamespace> | ||
<AssemblyName>KnapsackProblem.SimulatedEvolution</AssemblyName> | ||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
<TargetFrameworkProfile /> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<Prefer32Bit>false</Prefer32Bit> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<Prefer32Bit>false</Prefer32Bit> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="System" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="Chromosome.cs" /> | ||
<Compile Include="GeneticAlgorithm.cs" /> | ||
<Compile Include="GeneticAlgorithmArgs.cs" /> | ||
<Compile Include="Program.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
<Compile Include="Helpers.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\KnapsackProblem.Common\KnapsackProblem.Common.csproj"> | ||
<Project>{d69e0ecc-70c5-41c3-abba-e4f385e6982a}</Project> | ||
<Name>KnapsackProblem.Common</Name> | ||
</ProjectReference> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | ||
Other similar extension points exist, see Microsoft.Common.targets. | ||
<Target Name="BeforeBuild"> | ||
</Target> | ||
<Target Name="AfterBuild"> | ||
</Target> | ||
--> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Text; | ||
using KnapsackProblem.Common; | ||
|
||
namespace KnapsackProblem.SimulatedEvolution | ||
{ | ||
delegate TResult ParseDelegate<in T1, T2, out TResult>(T1 input, out T2 output); | ||
|
||
class Program | ||
{ | ||
private const int OptionPadding = 14; | ||
|
||
/* args: | ||
[0] - a path to directory with testing files | ||
[1] - how many files to use for testing | ||
[2] - how many times repeat a single file | ||
[3] - whether to save results | ||
[4 ...] - GA arguments | ||
*/ | ||
static void Main(string[] args) | ||
{ | ||
var testFiles = Common.Helpers.LoadTestFiles(args); | ||
if (testFiles == null) | ||
{ | ||
Console.ReadLine(); | ||
return; | ||
} | ||
|
||
var genAlgArgs = ParseArguments(args); | ||
if (genAlgArgs == null) | ||
{ | ||
Console.ReadKey(); | ||
return; | ||
} | ||
|
||
var knapsacks = KnapsackLoader.LoadKnapsacks(testFiles.First(), 1); | ||
var ga = new GeneticAlgorithm(genAlgArgs); | ||
var result = ga.Solve(knapsacks.First()); | ||
Console.WriteLine("BEST PRICE FOUND " + result.BestPrice); | ||
|
||
Console.ReadLine(); | ||
} | ||
|
||
#region Generator arguments | ||
|
||
static GeneticAlgorithmArgs ParseArguments(string[] args) | ||
{ | ||
if (args.Length == 0 || args[0] == "-?" || args[0] == "/?") | ||
{ | ||
PrintHelp(); | ||
return null; | ||
} | ||
|
||
try | ||
{ | ||
int populationSize = ParseInt32Option(args, "p", true, 0, int.MaxValue); | ||
int maxGenerations = ParseInt32Option(args, "g", true, 0, int.MaxValue); | ||
int tournamentSize = ParseInt32Option(args, "t", true, 0, populationSize); | ||
double elitismDegree = ParseDoubleOption(args, "e", true, 0, 1); | ||
|
||
return new GeneticAlgorithmArgs(populationSize, maxGenerations, tournamentSize, elitismDegree); | ||
} | ||
catch { return null; } | ||
} | ||
|
||
static int ParseInt32Option(string[] args, string option, bool compulsory, int lowerLimit, int upperLimit, int defaultValue = 0) | ||
{ | ||
return ParseOption(args, option, compulsory, int.TryParse, lowerLimit, upperLimit, defaultValue); | ||
} | ||
|
||
static double ParseDoubleOption(string[] args, string option, bool compulsory, double lowerLimit, double upperLimit, double defaultValue = 0) | ||
{ | ||
return ParseOption(args, option, compulsory, double.TryParse, lowerLimit, upperLimit, defaultValue); | ||
} | ||
|
||
static T ParseOption<T>(string[] args, string option, bool compulsory, ParseDelegate<string, T, bool> parse, T lowerLimit, T upperLimit, T defaultValue) where T : IComparable<T> | ||
{ | ||
option = "-" + option; | ||
for (int i = 0; i < args.Length; i += 2) | ||
{ | ||
if (string.Equals(args[i], option, StringComparison.InvariantCulture)) | ||
{ | ||
T value; | ||
if (!parse(args[i + 1], out value)) | ||
{ | ||
Console.WriteLine($"{option} option value is not a valid {typeof(T).Name} number."); | ||
throw new ArgumentException(); | ||
} | ||
|
||
if (value.CompareTo(lowerLimit) == -1 || value.CompareTo(upperLimit) == 1) | ||
{ | ||
Console.WriteLine( | ||
$"{option} option value is limited to range from {lowerLimit} to {upperLimit}."); | ||
throw new ArgumentException(); | ||
} | ||
|
||
return value; | ||
} | ||
} | ||
|
||
if (compulsory) | ||
{ | ||
Console.WriteLine($"Option {option} is compulsory but it's missing."); | ||
throw new ArgumentException(); | ||
} | ||
|
||
return defaultValue; | ||
} | ||
|
||
static void PrintHelp() | ||
{ | ||
var sb = new StringBuilder(); | ||
sb.AppendLine(); | ||
sb.AppendLine("Usage: -p size -g count -t size -e degree"); | ||
sb.AppendLine(); | ||
sb.AppendLine("Options:"); | ||
AppendOption("-p size", "Population size.", sb); | ||
AppendOption("-g count", "Total # of generations before stopping.", sb); | ||
AppendOption("-t size", "Tournament size.", sb); | ||
AppendOption("-e degree", "Percents of a population's fittest pass to the next generation.", sb); | ||
Console.WriteLine(sb.ToString()); | ||
} | ||
|
||
static void AppendOption(string optionName, string description, StringBuilder sb) | ||
{ | ||
sb.AppendLine($" {optionName.PadRight(OptionPadding)}{description}"); | ||
} | ||
|
||
#endregion | ||
} | ||
} |
Oops, something went wrong.