24hrs ago I asked a similar question (
here) but with regards to a straight line,thanks to
ppolymorphe for pointing it out, turns out I hadn't considered the return to starting point.
After fixing that and testing it out and I was happy with the solution,So I took the program to the next phase and used a scattered point instead of a straight line.
Problem:
It gave this :
For Scattered 10 Cities, it usually gives the output solution of the cost around 1319, which is pretty good, but occasionally gives an output of the cost around 1400 and sometimes even 1500.
10 Cities Optimum Solution
10 Cities Bad Solution
Whereas, for 20 Cities, it usually gives the bad solution of the cost around 2500-3000, and very rarely gives a good solution of the cost around 1700-1800.
20 Cities Bad Solution
20 Cities Optimum Solution
How do I improve on the output? I would like it to output more optimal solution.
I have also notice that due to numerous for loops, if I increase my population size, the program takes longer to execute. I will appreciate any suggestions on improving the code to decrease the execution time.
--------------------------------------------------------------------------------
I have made a minor change to the original project(link above) just so that I have a easier access to modifying the mutationRate,crossoverRate, and number of Cities.
City
public class City
{
public int X { get; set; }
public int Y { get; set; }
public City(int x, int y)
{
X = x; Y = y;
}
public int GetDistance(City otherCity)
{
int deltaX = X - otherCity.X;
int deltaY = Y - otherCity.Y;
return (int)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
}
}
Constant
public class Constant
{
public static List<City> CITIES = new List<City>()
#region
{
new City(6, 190),
new City(29, 73),
new City(47, 388),
new City(52, 65),
new City(75, 330),
new City(77, 5),
new City(93, 94),
new City(132, 380),
new City(389, 147),
new City(121, 251),
new City(230, 159),
new City(73, 227),
new City(317, 117),
new City(52, 248),
new City(265, 330),
new City(77, 5),
new City(393, 94),
new City(407, 380),
new City(401, 147),
new City(322, 251),
};
#endregion
public static double MUTATION = 0.50;
public static int POPULATION = 400;
public static int SAMECOUNT = 120;
public static double CROSSOVER = 0.6;
public static int TOURNAMENTSIZE = 40;
}
Chromosomes
public class Chromosomes
{
public static int DefaultGeneLength = Constant.CITIES.Count();
private int[] Genes = new int[DefaultGeneLength];
public Chromosomes()
{
Genes = Enumerable.Repeat(-1, DefaultGeneLength).ToArray();
}
public int[] GetGenes()
{
return Genes;
}
private int Fitness = 0;
public void GenerateRandom()
{
int timesZero = 0;
for (int i = 0; i < Size(); i++)
{
bool repeat = false;
do
{
int index = (int)(new Random(Guid.NewGuid().GetHashCode()).NextDouble() * (double)Size());
if(Genes.Contains(index))
{
repeat = true;
if(index == 0){
if(timesZero ==0)
{
timesZero = 1;
Genes[i] = index;
repeat = false;
}
}
}
else{
repeat = false;
Genes[i] = index;
}
}while(repeat == true);
}
if(Genes.Distinct().Count()<10)
{ }
}
public int GetGene(int index)
{
return Genes[index];
}
public void SetGene(int index, int value)
{
Genes[index] = value;
Fitness = 0;
}
public int GetFitness()
{
if (Fitness == 0)
{
Fitness = FitnessCalc.GetFitness(this);
}
return Fitness;
}
public int Size()
{
return Genes.Length;
}
public string Show(City[] cities)
{
StringBuilder sbuilder = new StringBuilder();
foreach (int i in Genes)
{
sbuilder.Append(cities[i].X + "," + cities[i].Y + ">>");
}
sbuilder.Append("Total Cost:" + GetFitness());
return sbuilder.ToString();
}
}
FitnessCalc
public class FitnessCalc
{
static public City[] cities;
public static void Initialize(City[] arrCities)
{
cities = new City[arrCities.Count()];
for (int i = 0; i < arrCities.Count(); i++)
{
cities[i] = arrCities[i];
}
}
internal static int GetFitness(Chromosomes chromosomes)
{
int count = chromosomes.GetGenes().Where(i => i == 0).Count();
if (count > 1)
return 99999;
int cost = 0;
for (int i = 0; i < chromosomes.Size() - 1; i++)
{
int dist = cities[chromosomes.GetGene(i)].GetDistance(cities[chromosomes.GetGene(i + 1)]);
cost += dist;
}
cost += cities[chromosomes.GetGene(chromosomes.GetGenes().Length - 1)].GetDistance(cities[chromosomes.GetGene(0)]);
return cost;
}
}
GA_TSPVER2:Genetic Algorithm Logic
public class GA_TSPVer2
{
private static double uniformRate;
private static double mutationRate;
private static int tournamentSize;
private static bool elitism = true;
public static Population Evolve(Population pop,double mutation,double crossover,int tournament)
{
mutationRate = mutation;
uniformRate = crossover;
tournamentSize = tournament;
Population newPopulation = new Population(pop.Size(), false);
if (elitism)
{
newPopulation.SaveIndividual(0, pop.GetFittest());
}
int elitismOffset;
if (elitism)
{
elitismOffset = 1;
}
else
{
elitismOffset = 0;
}
for (int i = elitismOffset; i < pop.Size(); i++)
{
Chromosomes indiv1 = TournamentSelection(pop);
Chromosomes indiv2 = TournamentSelection(pop);
Chromosomes newIndiv = Crossover(indiv1, indiv2);
newPopulation.SaveIndividual(i, newIndiv);
}
for (int i = elitismOffset; i < newPopulation.Size(); i++)
{
Mutate(newPopulation.GetIndividual(i));
}
return newPopulation;
}
private static Chromosomes Crossover(Chromosomes indiv1, Chromosomes indiv2)
{
Chromosomes newSol = new Chromosomes();
for (int j = 0; j < newSol.Size(); j++)
{
double toss = (new Random(Guid.NewGuid().GetHashCode()).NextDouble());
if (toss > uniformRate)
{
int Gene = 0;
int geneLeft = indiv2.GetGenes().Length - j;
int counter = j;
do{
if(geneLeft == 0)
{
Gene = indiv2.GetGenes().Except(newSol.GetGenes()).First();
}
else {
Gene = indiv2.GetGenes().ElementAt(counter++);
geneLeft--;
}
bool b = newSol.GetGenes().Contains(Gene);
}while(newSol.GetGenes().Contains(Gene));
newSol.SetGene(j, Gene);
}
else
{
int Gene = 0;
int geneLeft = indiv1.GetGenes().Length - j;
int counter = j;
do
{
if (geneLeft == 0)
{
Gene = indiv1.GetGenes().Except(newSol.GetGenes()).First();
}
else
{
Gene = indiv1.GetGenes().ElementAt(counter++);
geneLeft--;
}
bool b = newSol.GetGenes().Contains(Gene);
} while (newSol.GetGenes().Contains(Gene));
newSol.SetGene(j, Gene);
}
}
return newSol;
}
private static void Mutate(Chromosomes indiv)
{
for (int i = 0; i < indiv.Size(); i++)
{
double d = new Random(Guid.NewGuid().GetHashCode()).NextDouble();
if (d <= mutationRate)
{
int sindex = (int)(new Random(Guid.NewGuid().GetHashCode()).NextDouble() * indiv.Size());
int sValue = indiv.GetGene(sindex);
int eindex;
do
{
eindex = (int)(new Random(Guid.NewGuid().GetHashCode()).NextDouble() * indiv.Size());
} while (sindex == eindex);
int eValue = indiv.GetGene(eindex);
indiv.SetGene(sindex, eValue);
indiv.SetGene(eindex, sValue);
}
}
}
private static Chromosomes TournamentSelection(Population pop)
{
Population tournament = new Population(tournamentSize, false);
Population selection = new Population(tournamentSize/2, false);
var temp = pop.individuals.OrderBy(x => x.GetFitness()).Take(tournamentSize/2);
for (int i = 0; i < tournamentSize; i++)
{
double d = new Random(Guid.NewGuid().GetHashCode()).NextDouble();
int randomId = (int)(d * selection.Size());
tournament.SaveIndividual(i, temp.ElementAt(randomId));
}
Chromosomes fittest = tournament.GetFittest();
return fittest;
}
}
Population
public class Population
{
public Chromosomes[] individuals;
public Population(int populationSize, bool initialize)
{
individuals = new Chromosomes[populationSize];
if (initialize)
{
for (int i = 0; i < Size(); i++)
{
Chromosomes newIndividual = new Chromosomes();
newIndividual.GenerateRandom();
SaveIndividual(i, newIndividual);
}
}
}
public Chromosomes GetIndividual(int index)
{
return individuals[index];
}
public Chromosomes GetFittest()
{
return individuals.Aggregate((i1, i2) => i1.GetFitness() < i2.GetFitness() ? i1 : i2);
}
public int Size()
{
return individuals.Length;
}
public void SaveIndividual(int index, Chromosomes individual)
{
individuals[index] = individual;
individual.GetFitness();
}
}
Main Class
public class MyMain
{
public static void Main()
{
TSPVer2.FitnessCalc.Initialize(Constant.CITIES.ToArray());
TSPVer2.Population myPop = new TSPVer2.Population(Constant.POPULATION, true);
Console.WriteLine(myPop.GetFittest().GetFitness());
int generationCount2 = 0;
int sameCount2 = 0;
int thisCost2 = 0;
int oldCost2 = 0;
Console.WriteLine("");
while (sameCount2 < Constant.SAMECOUNT)
{
generationCount2++;
myPop = TSPVer2.GA_TSPVer2.Evolve(myPop, Constant.MUTATION,Constant.CROSSOVER,Constant.TOURNAMENTSIZE);
thisCost2 = myPop.GetFittest().GetFitness();
if ((int)thisCost2 == (int)oldCost2)
{
Console.SetCursorPosition(0, Console.CursorTop - 1);
ClearCurrentConsoleLine();
sameCount2++;
Console.WriteLine("Same:" + sameCount2 + " Cost" + thisCost2);
}
else
{
Console.SetCursorPosition(0, Console.CursorTop - 1);
ClearCurrentConsoleLine();
sameCount2 = 0;
oldCost2 = thisCost2;
Console.WriteLine("Old Cost:" + oldCost2 + ", New Cost:" + thisCost2);
}
}
Console.WriteLine("Gen:" + generationCount2);
Console.WriteLine(myPop.GetFittest().Show(TSPVer2.FitnessCalc.cities));
Console.Read();
}
public static void ClearCurrentConsoleLine()
{
int currentLineCursor = Console.CursorTop;
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition(0, currentLineCursor);
}
}
What I have tried:
I have tried changing the mutationRate, crossOverRate, populationsize