-
Notifications
You must be signed in to change notification settings - Fork 2
/
Tsp.cs
245 lines (212 loc) · 9.68 KB
/
Tsp.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
//////////////////////////////////////////////////////////////////////////////////////////////////
// File Name: Tsp.cs
// Permission to use, copy, modify, and distribute this Program and its documentation,
// if any, for any purpose and without fee is hereby granted, provided that:
// (i) you not charge any fee for the Program, and the Program not be incorporated
// by you in any software or code for which compensation is expected or received;
// (ii) the copyright notice listed above appears in all copies;
// (iii) both the copyright notice and this Agreement appear in all supporting documentation; and
// (iv) the name of Michael LaLena or lalena.com not be used in advertising or publicity
// pertaining to distribution of the Program without specific, written prior permission.
///////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using System.Data;
using System.Drawing;
namespace Tsp
{
/// <summary>
/// This class performs the Travelling Salesman Problem algorithm.
/// </summary>
class Tsp
{
/// <summary>
/// Delegate used to raise an event when a new best tour is found.
/// </summary>
/// <param name="sender">Object that generated this event.</param>
/// <param name="e">Event arguments. Contains information about the best tour.</param>
public delegate void NewBestTourEventHandler(Object sender, TspEventArgs e);
/// <summary>
/// Event fired when a new best tour is found.
/// </summary>
public event NewBestTourEventHandler foundNewBestTour;
/// <summary>
/// Random number generator object.
/// We allow the GUI to set the seed for the random number generator to assist in debugging.
/// This allows errors to be easily reproduced.
/// </summary>
Random rand;
/// <summary>
/// The list of cities. This is only used to calculate the distances between the cities.
/// </summary>
Cities cityList;
/// <summary>
/// The complete list of all the tours.
/// </summary>
Population population;
public static int generation;
/// <summary>
/// Private copy of a flag that will stop the TSP from calculating any more generations.
/// </summary>
private bool halt = false;
/// <summary>
/// The GUI sets this flag to true to stop the TSP algorithm and allow the Begin() function to return.
/// </summary>
public bool Halt
{
get
{
return halt;
}
set
{
halt = value;
}
}
/// <summary>
/// Default Constructor
/// </summary>
public Tsp()
{
}
/// <summary>
/// Starts the TSP algorithm.
/// To stop before all generations are calculated, set <see cref="Halt"/> to true.
/// </summary>
/// <param name="populationSize">Number of random tours to create before starting the algorithm.</param>
/// <param name="maxGenerations">Number of times to perform the crossover operation before stopping.</param>
/// <param name="groupSize">Number of tours to examine in each generation. Top 2 are chosen as the parent tours whose children replace the worst 2 tours in the group.</param>
/// <param name="mutation">Odds that a child tour will be mutated..</param>
/// <param name="iterationForConverge">Iteration for converge...</param>
/// <param name="seed">Seed for the random number generator.</param>
/// <param name="chanceToUseCloseCity">The odds (out of 100) that a city that is known to be close will be used in any given link.</param>
/// <param name="cityList">List of cities in the tour.</param>
public void Begin(int populationSize, int maxGenerations, int groupSize, int mutation, int iterationForConverge, int seed, int chanceToUseCloseCity, Cities cityList)
{
rand = new Random(seed);
this.cityList = cityList;
population = new Population();
population.CreateRandomPopulation(populationSize, cityList, rand, chanceToUseCloseCity);
displayTour(population.BestTour, 0, false);
bool foundNewBestTour = false;
TspForm tspform = new TspForm();
int ItterForStop = 0;
int Waiter = 0;
for (generation = 0; generation < maxGenerations; generation++)
{
Waiter += 1;
if (Waiter >= 100)
{
if (TspForm.iteration == 0 || generation >= 50 && generation <= maxGenerations/100 )
{
TspForm.iteration = generation;
}
if (TspForm.iteration != generation)
{
double stop_work = TspForm.iteration - generation / 1.4;
if (stop_work <= 0)
{
ItterForStop += 1;
}
}
if (ItterForStop == iterationForConverge)
{
Halt = true;//Break the algorithm if we found the best tour
}
}
if (Halt)
{
break; // GUI has requested we exit.
}
foundNewBestTour = makeChildren(groupSize, mutation);
if (foundNewBestTour)
{
displayTour(population.BestTour, generation, false);
}
}
displayTour(population.BestTour, generation, true);
}
/// <summary>
/// Randomly select a group of tours from the population.
/// The top 2 are chosen as the parent tours.
/// Crossover is performed on these 2 tours.
/// The childred tours from this process replace the worst 2 tours in the group.
/// </summary>
/// <param name="groupSize">Number of tours in this group.</param>
/// <param name="mutation">Odds that a child will be mutated.</param>
bool makeChildren(int groupSize, int mutation)
{
int[] tourGroup = new int[groupSize];
int tourCount, i, topTour, childPosition, tempTour;
// pick random tours to be in the neighborhood city group
// we allow for the same tour to be included twice
for (tourCount = 0; tourCount < groupSize; tourCount++)
{
tourGroup[tourCount] = rand.Next(population.Count);
}
// bubble sort on the neighborhood city group
for (tourCount = 0; tourCount < groupSize - 1; tourCount++)
{
topTour = tourCount;
for (i = topTour + 1; i < groupSize; i++)
{
if (population[tourGroup[i]].Fitness < population[tourGroup[topTour]].Fitness)
{
topTour = i;
}
}
if (topTour != tourCount)
{
tempTour = tourGroup[tourCount];
tourGroup[tourCount] = tourGroup[topTour];
tourGroup[topTour] = tempTour;
}
}
bool foundNewBestTour = false;
// take the best 2 tours, do crossover, and replace the worst tour with it
childPosition = tourGroup[groupSize - 1];
population[childPosition] = Tour.Crossover(population[tourGroup[0]], population[tourGroup[1]], cityList, rand);
if (rand.Next(100) < mutation)
{
population[childPosition].Mutate(rand);
}
population[childPosition].DetermineFitness(cityList);
// now see if the first new tour has the best fitness
if (population[childPosition].Fitness < population.BestTour.Fitness)
{
population.BestTour = population[childPosition];
foundNewBestTour = true;
}
// take the best 2 tours (opposite order), do crossover, and replace the 2nd worst tour with it
childPosition = tourGroup[groupSize - 2];
population[childPosition] = Tour.Crossover(population[tourGroup[1]], population[tourGroup[0]], cityList, rand);
if (rand.Next(100) < mutation)
{
population[childPosition].Mutate(rand);
}
population[childPosition].DetermineFitness(cityList);
// now see if the second new tour has the best fitness
if (population[childPosition].Fitness < population.BestTour.Fitness)
{
population.BestTour = population[childPosition];
foundNewBestTour = true;
}
return foundNewBestTour;
}
/// <summary>
/// Raise an event to the GUI listener to display a tour.
/// </summary>
/// <param name="bestTour">The best tour the algorithm has found so far.</param>
/// <param name="generationNumber">How many generations have been performed.</param>
/// <param name="complete">Is the TSP algorithm complete.</param>
void displayTour(Tour bestTour, int generationNumber, bool complete)
{
if (foundNewBestTour != null)
{
this.foundNewBestTour(this, new TspEventArgs(cityList, bestTour, generationNumber, complete));
}
}
}
}