Computer & Network Engineering
Introduction to Programming
(Java)
Konstantinos Vlahavas
The Travelling Salesman Problem
Introduction
The aim of this assignment is to create a program in Java, which will solve the
traveling
salesman problem. The defi
nition of this problem is as follows: A salesman must visit a number of
cities and he needs to find the shortest possible route that allows him to visit every city only once,
starting from his home and returning there at the end. An applet will be designed
, which will display
two square areas (maps) with circles representing all the cities, placed there at random. The first map
will have the initial route, created at random along with the cities and the second will demonstrate the
optimum route the computer
could find. The program will make use of two algorithms, Brute Force
and Simulated Annealing, a description of which is given.
Procedure
1.
Beginning of code
–
Variable declaration
The first three lines import the standard libraries, needed for creating a
n applet with a
graphical user interface. The name
TravellingSalesmanProblem
is given to the program and the
necessary listener for the G.U.I. is implemented. Next, the main variables of the program are
declared. The G.U.I. will need 4 panels, 2 labels, 2
checkbox groups (one includes 2 radio buttons
and the other 4), 3 checkboxes and a button (Picture 1). The string
algorithm
will be used by the
paint
method to determine which algorithm to use. The values it can take are
“
bruteforce
”
and
“
annealing
”
, with
the first as default. The integer
N
will store the number of cities.
10
is the default
value. The arrays
x[ ]
and
y[ ]
will store the respective coordinates of the cities. They contain
50
entries, because it is the maximum number of cities used in the prog
ram.
The
route[
]
array will store
the order in which the cities are to be visited. It contains 51 entries, because the last will be used for
the home at the end of the route.
The last 6 variables will be used to display the mean effectiveness of the 2 alg
orithms on the
screen (below the maps). The effectiveness is calculated from the ratio of the amount by which the
initial total distance is reduced, over the initial total distance. This ratio is stored in the
BF
improvement[ ]
or
SAimprovement[ ]
, dependin
g on the algorithm. The
BF
executions
or
SAexecutions
counts the number of times each algorithm is executed. The sum of all the elements of
the arrays is stored in
total1
or
total2
, and the
BF
executions
or
SAexecutions
is used to divide the
respective total
, resulting in the mean value of the effectiveness.
The 2 arrays contain 100 entries,
because there is no need for more, since the number representing the effectiveness will have
stabilized by the 100th test, especially if the same map is being tested. The
paint
method, where
these calculations take place, contains
an
if
statement,
which stops adding any more values to them.
2.
Init method

The Graphical User Interface
At the beginning of the
init
method, the background of the program window is set to
white
,
to prevent the default gray which is used by web browsers. The G.U.I. makes use of the
Gridbag
Layout manager and it is contained in a separate panel, so that it occupies the top of the window and
not the center. This panel has the
lightGray
color as a b
ackground. The components are then added
to the gridbag cells from left to right. The 2 groups of radio buttons and the group of checkboxes are
added to a different panel first, which uses the grid layout to position the components vertically. The
Start
bu
tton uses internal padding to increase its size, so that it is easier to press, because it is the
component that will be used the most. External padding is also used to separate the 4 different groups
of components: type of algorithm, number of cities, opt
ions and the
Start
b
utton. The G.U.I. is now
ready!
Picture 1. The G.U.I.
The user can choose the type of algorithm to be used (
Brute Force
is the default) and the
number of cities (
10
is the default). The
Start
button will then instr
uct the program to solve the
traveling
salesman problem using the selected algorithm and number of cities. Three options are
provided in the form of checkboxes. The default state for all of them is
false
. The first will “lock” the
position of the cities on
the current map, allowing the user to try the other algorithm on the same
map, or simply find a different solution. The second option will display numbers next to the cities,
according to the order in which they were created. It can be used to find which
city is visited at any
point of the route, but it also fills the maps with numbers, especially when using 30 or 50 cities, so its
use is only recommended together with the third option. The last checkbox will only display lines
between the cities, instead
of a line with a red triangle at the end, which points to the direction of the
route at that point. It is useful together with the previous option or when the user wants the minimum
amount of information on the maps.
3.
ActionPerformed method
–
Using the Gr
aphical User Interface
This method is used each time the
Start
button is pressed, which is the only component of the
G.U.I. that has a listener. First of all, it will find which algorithm is currently selected and will update
the value of
algorithm
, if ne
cessary. Then it will do exactly the same for the number of cities. This
time though, if
check1
(
Lock the current map
) is true, it will not update the value of
N
. Instead, it
will make sure the selection in the G.U.I. is returned to the previously locked n
umber. The state of
the other two options is checked by other methods directly. Finally, it restarts the
paint
method.
4.
Circle method
–
Displaying the cities
This method simplifies the creation of a circle by adjusting the parameters used in the
fillOval
method. The three numbers it needs are the x and y coordinates of the center of the circle
and the radius of it. It expresses the coordinates of the starting point and the width and height of the
oval as a function of the desired center and radius of the c
ircle.
5.
drawArrow
method
–
Drawing arrows along the route
This method will connect 2 cities by drawing an arrow, which shows the order in which t
hese
cities are visited.
It also helps to make out the route through a group of cities that happened to be
pla
ced too close to one another.
The arrow consists of a simple line with a filled red triangle at its
end. All this method needs is the coordinates of the centers of the 2 cities, given in the right order,
and the radius of the circles. It starts by drawing
the line, which connects the centers, using the
drawLine
method. The size of the triangle and its coordinates are declared as integers. Next, it
calculates the angle between the line and the x

axis. The
Math.atan
method uses a tangent and
returns the equiv
alent angle. The tangent of the angle, which any line between two points, (
x1
,
y1
)
and (
x2
,
y2
), forms with the x

axis, is given by
x1
x2
y1
y2
. The results of the subtractions are converted
to doubles because a division between integers would re
turn an integer.
If the line is vertical to the x

axis,
then the tangent is equal to infinity, which means that the angle is
9
0
o
.
The
sine and the cosine
of the angle are then calculated. The next step involves two independent changes in the axes system,
t
o produce a much simpler one. As shown in Figure 1, the program can now convert complex
coordinates (colored blue) to the ones used in the imaginary axes system (colored red) and then
convert them back to be used for the triangle.
The first change is cal
led “parallel transfer” and it refers to the horizontal and then the
vertical transfer of the axes, so that the origin now coincides with (
x1
,
y1
). This can be achieved by
subtracting the value of
x1
from all the x

coordinates and the value of
y1
from all t
he y

coordinates.
The opposite is required to convert them back to the original axes system. The second change, which
happens next, is called “turning” and it is realized by turning the now transferred x

axis to an angle
equal to the one which has already
been found, so that the new X

axis now coincides with the line
connecting the 2 cities. It can be proven that all the new coordinates are now given by the following
formulas:
X
=
x
*cos(angle)+
y
*sin(angle)
Y
=
y
*cos(angle)

x
*sin(angle)
Once all the calculatio
ns in the new axes system have finished, the following formulas
convert the coordinates back:
x
=
X
*cos(angle)

Y
*sin(angle)
y
=
X
*sin(angle)+
Y
*cos(angle)
Of course, in this case every point has undergone through the “parallel transfer” process first,
so each
x
will be
x

x1
and each
y
will be
y

y1
in the above four formulas.
It is now obvious that all the points of the line have their Y

coordinate equal to
0
. The process
of finding (
x3
,
y3
) is now as simple as subtracting the value of
r
from
X2
,
which results i
n (
X2

r
,
0
).
Similarly, the other 2 points are (
X2

(r+size)
,
size/2
)
and (
X2

(r+size)
,

size/2
). Everything the
program
needs
is now a function of
X2
, which can be found using the first formula:
X
2
=(
x
2

x1)
*cos(angle)+(
y
2

y1)
*sin(angle)
A this point, t
he coordinates can have numeric values, so all that is left is to use the third and
the fourth formula to convert them back (adding
x1
or
y1
respectively, to reverse the “parallel
transfer”). In the program code, all the conversions are made in the same li
ne by substituting the first
two formulas in the last two. Then they are rounded to the nearest integer using
Math.round
. If
x2
is
less than
x1
, then
X2
will be negative, therefore,
r
and
size
will have to be added to
X2
and not
subtracted
in that case. Th
e
fillPolygon
method can now make use of the three points to create the
red arrowhead.
angle
(
x5,y5
)
City 2
r
City 1
size/2
0
[(
x
2
,y
2
),(
X2,0
)]
[(
x1,y1
),(
0,0
)]
x
y
Y
size
X
(
x4,y4
)
(
x
3
,y
3
)
Figure 1. The drawArrow method
The reason this conversion is so useful is because it helps to avoid having to solve a pair of
simultaneous quadratic equations, which would result from Pythagoras theor
em. A more useful
application of this procedure is used later in the
DetectCrossing
method.
The
drawArrow method is used by default, but it can be ignored by clicking on
check3
(
Hide
arrowheads
).
6.
DisplayRoute
method
–
Forming the route on the maps
This m
ethod contains a
for
loop, which counts up to
N

1
and draws lines or the arrows along
the route, depending on the state of
check3
. To use the coordinates of the cities
in the right order
, it
uses the value of the
route[ ]
array as the index of the
x[ ]
and
y[ ]
arrays. The value of the
counter
provides the necessary index for the
route[ ]
array. All the lines use the
gray
color. The variable
a
is
used to control which map the route will be displayed on and the values it can take are
0
and
390
.
7.
LineLength m
ethod
–
Finding the distance between two cities
This method needs the coordinates of the centers of two cities and it uses the Pythagoras
theorem to find the distance between them. It returns a double.
8.
CalculateTotalDistance
method
–
Finding the total di
stance
This method uses the
LineLength
method and adds all the results to find the total distance of
the current route. It contains a similar loop to the one used in the
DisplayRoute
method. The result is
rounded and an integer is returned.
9.
DetectCrossin
g
method
–
Discovering the unwanted crossings
This method uses the same principle as in the
drawArrow
method. It changes the axes
system twice, simplifies the coordinates and finds out if two lines cross each other or not. This time
though, it is not nec
essary to convert the new coordinates of the points back to the original axes
system, because if it can be proven that the lines have a common point
, it doesn
’
t matter what the
real coordinates of that point are. The 8 variables
(2 for each point) are con
verted to doubles for
better accuracy in the calculations, since they will not be used to draw anything on the maps. Initially
the angle between the first line and the x

axis is found and this in turn is used to find the sine and the
cosine of it. Then the
conversion of the coordinates takes place, using the first two formulas again.
City 3
City 1
Figure
2
–
The DetectCrossing method
Equation:
Y
=((Y4

Y3)/(X4

X3))*(X

X3)+Y3
City 2
(
X,0
)
0
[(
x
2
,y
2
),(
X2,0
)]
[(
x1,y1
),(
0,0
)]
x
y
Y
angle
X
[(
x4,y4
),(
X4,Y4
)]
[(
x
3
,y
3)
,(
X
3
,Y
3
)]
Equation:
Y=0
City 4
The new coordinates enable the equation of the second line
to be expressed
in the imaginary
axes system.
If
Y3
and
Y4
have the same sign (both positive
,
or both negative), the
n
the line doesn’t
cross the X

axis.
If they are both
0
, then the lines
may
coincide and the method was made to
search
for all 8 different possible positions
. For every other case, the equation has only one solution for
Y=0
which is as follows:
Y3
Y4
X3
X4
*
Y3
X3
X
The only thing that remains now is to check whether X is between
0
and
X2
, including those
.
There are two cases, depending on whether
X2
is positive or negative, so the program checks for
both. The two possible
returned values from this metho
d are
0
for no crossing and
1
for a detected
crossing.
10.
FindCrossings
method
–
Searching the whole route for crossings
The
DetectCrossing
method is mainly used here to search the current route and return the
number of crossings it found.
It takes every li
ne in the route and checks it with all the remaining lines
in the route after it, without including the one next to it. The check of the first line with the last is
prevented.
The lines
N

2
and
N

1
will have already been checked with every other line, by t
he time
the procedure will reach them, so they are not included in the loop.
11.
Paint method
–
The core of the program
Beginning
The
paint
method uses a number of new variables, which don’t have to be visible by other
methods and need to be reset to their
initial value every time
paint
is executed.
Distance
will store
the total distance of the initial route, while
Distnew
will store the total distance of every new route
that is being created by the algorithms. The purpose of
Distold
is to be replaced by the
value of
Distnew
, whenever this is less than its current value.
The
crossings
variable
stores the number of
crossings in the current route
and it
is initialized with a value other than zero (
1
), because
zero is the
condition on which the algorithms stop.
The variable
T
, which is initially set to
100
, is
the
basis of
the simulated annealing algorithm
.
Route[N]
refers to the
N+1
element of the
route[ ]
array and its
value (
0
) means that after the last city that was visited, the salesman will return to his ho
me. Two
rectangle areas, which will represent the maps, are then drawn to the screen and a name for e
ach is
written on top of them (
INITIAL ROUTE
and
FINAL ROUTE
). The cities require a 300x300 area
and the maps have a margin of 5 pixels to include the whol
e area of those cities that will be placed at
the edges of the rectangles.
The next step uses a
for
loop with a
counter
that increases from
0
to
N

1
. At the beginning of
the loop, the coordinates of a city are being created at random in the range 0

100 and
are then
multiplied by
3
to make the map appear to be 300x300, which is much more convenient. If 2 cities
happen to be placed too close to each other, the difference is now visible. A number is added to the
coordinates to move them to the location of the
first map. Each city created by the loop is then
compared with all the previous cities to ensure that it is not placed on top of another. If this happens,
the last step of the loop is repeated, until the coordinates of that city ar
e overwritten by new ones
.
The
Circle
method is then used to draw the city
using the coordinates from the
x[ ]
and
y[ ]
arrays as
the cente
r. The radius of the circle is
5
. By increasing the x

coordinates, a mirror of the first map is
shown on the second rectan
gle. Depending on th
e state of
check2
(
Display numbers
), numbers can
be drawn at the right of each city, using the magenta color. The last step of the loop creates the initial
route, which is always
0,1,2,…,N

1,0
. This means t
hat, starting and finishing at
0
(the home), the
s
alesman visits all the cities in the order in which they were created.
Once the loop finishes, the
DisplayRoute
method connects all the cities together accor
ding to
the initial route. The
CalculateTotalDistance
method is called next and it returns the tota
l distance
traveled by the salesman
, which is saved in both
Distance
and
Distold
.
Brute Force Algorithm
If the sele
cted algorithm in the G.U.I is
Brute Force
, then this part of the
paint
method will
be executed
next. It is contained within a
while
loop t
hat will stop repeat
ing the algorithm when the
FindCrossings
method reports that it did not find any crossings. The
route
[ ]
and
Distold
variables
ar
e reset to their initial values, each time crossings were detected.
The main procedure happens
within
a
for
loop, which is repeated
10,000
times. Two new integers,
a
and
b
are created next, which
are assigned
random
values in the range
1

(
N

1
)
. A
while
loop prevents
b
from being equal to
a
.
These are used to swap the order in which th
e cities
in the respectiv
e positions of the route
are
visite
d. This means that the entries
route[a]
and
route[b]
swap their value
, but only after the
ir
previous value is stored in
previous_route_a
and
previous_route_
b
.
Once the n
ew route has been
defined, the
CalculateTotalDistanc
e
method is called aga
in and it stores the result in
Distnew
. If the
new route is shorter than
,
or equal to
the previous, then
Distnew
is less than
, or equal to
Distold
and
it replaces it.
If it isn’t shorter, then the route is restored to its previous sta
te. This process will
eventually find a much more efficient route
and the loop will stop
.
The
FindCrossings
method is
now called to
check whether
there are
any
crossings of paths in th
e
new route
or not
.
If there are,
the
while
loop will repeat the whole a
lgorithm again, until a zero is returned. At this point, the job of the
algorithm is finished.
The percentage of improvement of the initial total distance is calculated next
(the values are converted to doubles before the division) and the r
esult is stored
in an entry of
BFimprovement[ ]
, depending on
BFexecutions
, which is also incremented by one afterwards.
Finally,
the sum of all the elements of
BFimprovement[ ]
is found.
Picture 2.
The solution of the
traveling
salesman problem for 20
cities, using ‘Brute Force’
Simulated Annealing Algorithm
The second option in the menu will force the program to use this
part of the
paint
method
. It
is base
d in the same principle as the
Brute Force
algorithm
and it u
ses the
T
variable, which is res
et
to
100
at the beginning
. This time though, if
Distnew
is greater than
Distold
, there is still a chance
that it will replace it. This depends on a probability which the program calculates using the following
formula:
probability=
e

(Distnew

Distold)/T
This always results in a number between
0
and
1
, without including
1
. To use
this probability,
the variable
dice
is created, which is assigned
a random val
ue in the exact same range. If
dice
is less
than or
equal to the probability, then
Distnew
replaces
D
istold
, even though the route is not shorter.
Furthermore, the main loop is only repeated 30 times
, but it is contained within a
while
loop, which
updates the value of
T
by multiplying it with
0.99
and starts the
for
loop again, until ‘
T<=0.1
’.
The
exact s
ame procedure
regarding any possible crossings
takes
place and, once the final route is found,
the percentage of improvement is calculated and processed in the same way.
Picture
3
. The solution of the
traveling
salesman problem for
3
0 cities, using ‘
S
imulated Annealing’
Displaying the solution
At this stage, the problem is solved so
all that remains is to use the
DisplayRoute
method, to
draw the final route on the second map. The
now greatly reduced total distance is shown below it.
The circle that r
epresents the home is painted again in blue on both maps. This makes it more
distinguishable and because
this happens at the end of the
paint
method
, it overwrites any arrow,
number or
line that happened to
pass through it.
An explanation for this is added
below the first map.
Finally, the mean value of all the elements of
BFimprovement[ ]
and
SA
improvement[ ]
is
displayed under the maps, together with the last percentage of improvement that eac
h algorithm
produced
.
This informa
tion is updated every time th
e
Start
button is pressed
, but it is lost if the
applet is restarted
.
Picture 4.
Data representing the effectiveness of the two algorithms
12.
The HTML file
To use the now compiled applet, an html file is required. The
<applet>
tag directs the
browsers t
o the
T
ravelling
S
alesman
P
roblem.class
file and defines a size for it, suitable for
800x600 resolutions.
The use of full screen mode in the browsers is still recommended
in this case
though
.
The
<center>
tag forces the applet to appear in the center of the
screen.
A double click on the html file
will start the program!
Picture 5. The program window in
A
pplet
V
iewer
Testing the program

Discussion
White Box
Tests
The procedure called white
box testing t
ook
place during
and after
the codin
g of the program.
Care was taken to ensure that all the methods in the code were tested separately, as they were
developed.
More emphasis was given to the methods
that are used more frequently.
Using random
values, it was verified that all methods are work
ing correctly, even with a range of numbers that they
will not be required to work with.
To test specific values, different
number categories were
considered, such as
0
,
single
digit, 2

digit and 3

digit numbers, as well as their negative values.
A
possibl
e division with
0
has been taken into consideration throughout the program and it was
prevented in those cases that it would cause undesired operation.
A
few problems that
existed
in the
drawArrow
and in the
DetectCrossing
method
were revealed and correcte
d during those tests.
Once the program was ready, it had to be tested as a whole.
The size of the program code
made it possible to achieve full
statement coverage
. Variables were placed throughout the code, at
least one in every method
and separate ones in
every
if
statement or
loop (or in the different levels of
a loop). Their purpose was to
be
increment
ed
by 1 each time the execution of the program
required
that part of the code and their values were printed on a separate part of the screen
at the end.
Us
ing
combinations of all the options in the G.U.I., every variable was displayed on the screen and the
program worked as intended, without throwing any exceptions.
For example, it was verified that
when a city was being created on top of another, that loop
was repeating itself once more, to prevent
this. The
FindCrossings
method should cause the wh
ole algorithms to repeat
themselves and indeed,
the number
of times they were executed was
a multiple of 10,000 for
Brute Force
and a multiple of
20,640
for
Simula
ted Annealing
.
Furthermore
, the code was
temporarily
modified to allow
5
and
100
cities as an option.
Both worked correctly and without exceptions, but while the solution for 5
cities was displayed on the screen
almost
as soon as the
Start
button was press
ed, for 100 cities the
program could not find a solution even after 15 hours, at which point it was interrupted,
to allow time
for
more useful tests.
Black Box Tests
Blackbox testing is equally important to whitebox testing. The actual code is of no inte
rest at
this stage.
The program has to be compared with the instructions/s
pecification
s
sheet, which
was
given before the
writing of the code.
By p
ressing the
Start
button a few times,
it becomes obvious
that the solution found by the program is not always
as efficient as it could be, especially when trying
large maps (30 or 50 cities).
Simple observation is enough to find a better route and it can also be
proven by keeping the same map and trying again 2 or 3 times.
This happens mainly because there is
no
description of the perfect solution, in mathematical terms, implemented in the program.
A series of tests reveals that the
Simulated Annealing
algorithm is noticeably better than
Brute Force
:
Time needed to solve the problem of 50 cities
00:00
00:14
00:28
00:43
00:57
01:12
01:26
01:40
1
2
3
4
5
6
7
8
9
Tests on different maps
Brute Force
Simulated Annealing
Improvement results for 10 cities
45%
46%
46%
47%
47%
48%
48%
49%
1
2
3
4
5
6
7
8
9
10
Tests on the same map
Brute Force
Simulated Annealing
Improvement results for 20 cities
44%
46%
48%
50%
52%
54%
56%
58%
60%
62%
1
2
3
4
5
6
7
8
9
10
Tests on the same map
Brute Force
Simulated Annealing
Improvement results for 30 cities
56%
58%
60%
62%
64%
66%
68%
70%
72%
1
2
3
4
5
6
7
8
9
10
Tests on the same map
Brute Force
Simulated Annealing
Improvement results for 50 cities
58%
60%
62%
64%
66%
68%
70%
72%
74%
76%
78%
1
2
3
4
5
6
7
8
9
10
Tests on the same map
Brute Force
Simulated Annealing
In a random map with 10 cities, the second
algorithm was able to find the best solution
(sometimes inverted), for all of the 10 tests.
For 8 out of the 10 tests, the route that
Brute Force
suggested was slightly less efficient.
Both of them were able to find the solutions within
3
seconds
at
most
.
There was no noticeable difference in their speed.
In a map with 20 cities,
the percentages of
improvement increased significantly for both algorithms, due to the increased complexity of the
initial route.
Simulated Annealing
gave a better solution in nin
e tests, but it was not as stable as
before.
There is still no difference in their speeds, but more time is necessary for them to find a
solution, around
5
seconds.
For 30 cities, the average improvement increased even more, but the
solutions that the algo
rithms found were clearly not the most efficient
, especially those found by
Brute Force
, which needed as much as 30 seconds for some of them
.
The second algorithm rarely
needed more than 10 seconds.
The most complex map
s
in the tests w
ere
the random map
s
w
ith 50
cities (
example in
Picture 5).
The routes that were found were
a greater improvement from the initial
route
s
, compared to the other maps, but they were nowhere near the best possible route.
Interesting
results were obtained from the
speed comparison
. The
Brute Force
algorithm needed 1 hour and 31
minutes to find one of the solutions, with an average of 1 hour and 11 minutes
, while
Simulated
Annealing
managed to find two of the solutions in 3 seconds and 10 seconds respectively, with an
average of 6 m
inutes!
Unfortunately, a problem which causes the area that the paint method uses to be reloaded
whenever another wind
ow passes on top of it, prevented
more tests because that meant that the PC
should be devoted solely to this.
The fact that
Brute Force
re
peats its main loop 10,000 times, while
Simulated Annealing
does that 20,640 times, reveals that the second algorithm manages to find a
route without crossings (generally a better route)
much faster than the first
, meaning that it needs to
be re

executed f
ewer times
.
The results of these tests were largely based on luck, as far as both the
location of the cities on the map, which affects the complexity of the initial route, and the “path” of
combinations that the algorithms choose to check is concerned. Tim
e on the other hand, depends
mainly
on the selected algorithm, which starts to have a significant impact
with maps of 30 cities or
more.
Another factor
that affects time
was made apparent with the
last
series of tests.
The program was written and compiled
in a PC equipped with an Intel Celeron 400 MHz
processor,
128 MB of RAM
and
Windows XP
.
All the above tests were performed in the same
computer as well. As part of the blackbox testing
, the program was tested
in
an Intel Pentium 3 450
MHz
, with
128 MB of R
AM
and
Windows 98
(not
SE
), and in an Intel Pentium 3 1 GHz, with 512
MB of RAM and Windows NT 4. The program worked as expected on all three machines, but the 1
GHz processor was quite faster in all aspects of the program.
Conclusions
The
Simulated Anne
aling
algorithm is better than the
Brute Force
algorithm, in terms
of both effectiveness and speed.
Whenever a solution is not satisfactory enough,
clicking on
the first
checkbox, “
Lock the current map
”, and trying again using that algorithm is recommended
.
The
reason for this is that this algorithm has a way of getting out of a particular path of combinations that
w
ould
not allow it to improve the route
.
The speed of the algorithms is
also
affected by the number of times their main loop is
repeated. Choosi
ng the number 10,000 for the
Brute Force
algorithm
provided a balance between
speed and a number sufficiently large for the algorithm to achieve an acceptable improvement of the
route (no crossings).
Choosing
3
,000 or 3
0
,000 made the algorithm much slower
,
each for a different
reason.
Trying all the possible combinations of routes would certainly find the most efficient
route, but even for a problem of 10 cities, the number of different routes is
362
,
880
(the factorial of 9
–
home is not included
), mean
ing
that the algorithm would be
1
7
times slower than
Simulated
Annealing
(at most)
.
For 50 cities, the number has 63 digits and for 17
2
or more cities, Java simply
returns “
Infinity
”!
Bibliography
VARIOUS AUTHORS (1999). Java 2 COMPLETE. SYBEX Inc.
Java 2
SDK, Standard Edition Documentation Version 1.3.1
.[online]. Last accessed on 6
February 2002 at URL:
http://java.sun.com/j2se/1.3/docs.html
VARIOUS AUTHORS (1997). MATHEMATICS (ALGEBRA
–
ANALYTICAL
GEOMETRY
–
PROBABILITIES), 3
rd
grade of
H
igh school
. 6
th
ed, OEDB, Athens.
CEM KANER, JACK FALK, HUNG QUOC NGUYEN (1993). TESTING COMPUTER
SOFTWARE. 2
nd
ed, International Thompson Publishing.
Program listing
–
TravellingSalesmanProblem
.java
/*
The Travelling Salesm
an Problem
Author: Konstantinos Vlahavas
Last Modified: 4/4/2002 */
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
public class TravellingSalesmanProblem extends Applet implements ActionListener {
Panel panel1=new Panel();
La
bel label1=new Label("Choose algorithm:");
Panel panel2=new Panel();
CheckboxGroup group1=new CheckboxGroup();
Checkbox radio1=new Checkbox("Brute Force",true,group1);
Checkbox radio2=new Checkbox("Simulated Annealing",false,group1);
Label label2=new
Label("Choose the number of cities:");
Panel panel3=new Panel();
CheckboxGroup group2=new CheckboxGroup();
Checkbox radio3=new Checkbox("10",true,group2);
Checkbox radio4=new Checkbox("20",false,group2);
Checkbox radio5=new Checkbox("30",false,group2)
;
Checkbox radio6=new Checkbox("50",false,group2);
Panel panel4=new Panel();
Checkbox check1=new Checkbox("Lock the current map");
// Locks the positions of the cities
Checkbox check2=new Checkbox("Display numbers");
// Shows numbers next to the citie
s
Checkbox check3=new Checkbox("Hide arrowheads");
// Ignores the drawArrow method
Button button1=new Button("Start");
String algorithm="bruteforce";
// The type of algorithm to be used, "bruteforce" or "annealing"
int N=10;
// Number of cities
i
nt x[]=new int[50];
// x coordinates of the cities
int y[]=new int[50];
// y coordinates of the cities
int route[]=new int[51];
// The order in which the cities are visited
int BFexecutions=0;
// Number of times the brute force algorithm was exec
uted
int BFimprovement[]=new int[100];
// Stores the percentages of improvement of the brute force algorithm
int total1=0;
// The sum of the elements of BFimprovement[], helps to find the mean value
int SAexecutions=0;
// Number of times the simula
ted annealing algorithm was executed
int SAimprovement[]=new int[100];
// Stores the percentages of improvement of the simulated annealing algorithm
int total2=0;
// The sum of the elements of SAimprovement[], helps to find the mean value
public voi
d init () {
// The Graphical User Interface
setBackground(Color.white);
GridBagLayout gridbag=new GridBagLayout();
GridBagConstraints c=new GridBagConstraints();
panel1.setLayout(gridbag);
panel1.setBackground(Color.lightGray);
c.gridx=0;
c
.gridy=0;
gridbag.setConstraints(label1,c);
panel1.add(label1);
c.insets=new Insets(0,0,0,20);
c.gridx=1;
c.gridy=0;
gridbag.setConstraints(panel2,c);
panel2.setLayout(new GridLayout(2,1));
panel2.add(radio1);
panel2.add(radio2);
panel
1.add(panel2);
c.insets=new Insets(0,0,0,0);
c.gridx=3;
c.gridy=0;
gridbag.setConstraints(label2,c);
panel1.add(label2);
c.insets=new Insets(0,0,0,20);
c.gridx=4;
c.gridy=0;
gridbag.setConstraints(panel3,c);
panel3.setLayout(new GridL
ayout(4,1));
panel3.add(radio3);
panel3.add(radio4);
panel3.add(radio5);
panel3.add(radio6);
panel1.add(panel3);
c.gridx=5;
c.gridy=0;
gridbag.setConstraints(panel4,c);
panel4.setLayout(new GridLayout(3,1));
panel4.add(check1);
panel
4.add(check2);
panel4.add(check3);
panel1.add(panel4);
c.insets=new Insets(0,0,0,5);
c.gridx=6;
c.gridy=0;
c.ipadx=20;
c.ipady=30;
gridbag.setConstraints(button1,c);
panel1.add(button1);
add(panel1);
button1.addActionListener(this)
;
}
public void actionPerformed (ActionEvent e) {
// The Start button restarts the paint method
if (group1.getSelectedCheckbox()==radio1) {
algorithm="bruteforce";
}
else {
algorithm="annealing";
}
if (check1.getState()!=true) {
// Cha
nges the number of cities if check1 is false
if (group2.getSelectedCheckbox()==radio3) {
N=10;
}
if (group2.getSelectedCheckbox()==radio4) {
N=20;
}
if (group2.getSelectedCheckbox()==radio5) {
N=30;
}
if (group2.getSelected
Checkbox()==radio6) {
N=50;
}
}
else {
// Returns the number of cities to the locked selection
if (N==10) {
group2.setSelectedCheckbox(radio3);
}
if (N==20) {
group2.setSelectedCheckbox(radio4);
}
if (N==30) {
group2.
setSelectedCheckbox(radio5);
}
if (N==50) {
group2.setSelectedCheckbox(radio6);
}
}
repaint();
}
public void Circle (Graphics g,int x,int y,int r) {
// Represents the cities, r=radius
g.fillOval(x

r,y

r,2*r,2*r);
}
public void dra
wArrow (Graphics g,int x1,int y1,int x2,int y2,int r) {
// Draws an arrow to show the direction, r is the radius of the cities
g.drawLine(x1,y1,x2,y2);
// The line connecting the two cities
int size=10;
// Size of the arrowhead
int x3,y3,x4,y4,x5,
y5;
// The coordinates for the arrowhead (triangle)
double angle=Math.atan((double)(y2

y1)/(double)(x2

x1));
// The angle between the line and the x

axis
double cos=Math.cos(angle);
double sin=Math.sin(angle);
double X2=(x2

x1)*cos+(y2

y1)*sin;
/
/ The X coordinate of (x2,y2) on an imaginary axes system
double Y2=(y2

y1)*cos

(x2

x1)*sin;
// The Y coordinate of (x2,y2) on an imaginary axes system (Always 0)
if (x2>=x1) {
// Calculates the coordinates by positioning the line on the X

axis of th
e imaginary axes system
x3=x1+(int)Math.round(((X2

r)*cos)

Y2*sin);
y3=y1+(int)Math.round(((X2

r)*sin)+Y2*cos);
x4=x1+(int)Math.round(((X2

(size+r))*cos)

(Y2+size/2)*sin);
y4=y1+(int)Math.round(((X2

(size+r))*sin)+(Y2+size/2)*cos);
x5=x1+(in
t)Math.round(((X2

(size+r))*cos)

(Y2

size/2)*sin);
y5=y1+(int)Math.round(((X2

(size+r))*sin)+(Y2

size/2)*cos);
}
else {
// Corrects the direction of the arrowhead
x3=x1+(int)Math.round(((X2+r)*cos)

Y2*sin);
y3=y1+(int)Math.round(((X2+r)*sin)+
Y2*cos);
x4=x1+(int)Math.round(((X2+(size+r))*cos)

(Y2+size/2)*sin);
y4=y1+(int)Math.round(((X2+(size+r))*sin)+(Y2+size/2)*cos);
x5=x1+(int)Math.round(((X2+(size+r))*cos)

(Y2

size/2)*sin);
y5=y1+(int)Math.round(((X2+(size+r))*sin)+(Y2

size/2)*c
os);
}
g.setColor(Color.red);
int x[]={x3,x4,x5};
int y[]={y3,y4,y5};
g.fillPolygon(x,y,3);
// Draws the arrowhead in red
}
public void DisplayRoute (Graphics g,int a) {
// Connects all the cities together according to the current route, "a" c
ontrols which map they will
// be
displayed on
for (int counter=0;counter<=N

1;counter++) {
g.setColor(Color.gray);
if (check3.getState()==false) {
drawArrow(g,x[route[counter]]+a,y[route[counter]],x[route[counter+1]]+a,y[route[counter+1]],5
);
}
else {
g.drawLine(x[route[counter]]+a,y[route[counter]],x[route[counter+1]]+a,y[route[counter+1]]);
}
g.setColor(Color.black);
}
}
public double LineLength (int x1,int y1,int x2,int y2) {
// Length of a line, using Pythagoras theo
rem
double l=Math.sqrt(Math.pow((x2

x1),2)+Math.pow((y2

y1),2));
return l;
}
public int CalculateTotalDistance () {
// Finds the total distance of the current route
int a=0;
for (int counter=0;counter<=N

1;counter++) {
a=a+(int)Math.round(L
ineLength(x[route[counter]],y[route[counter]],x[route[counter+1]],y[route[counter+1]]));
}
return a;
}
public int DetectCrossing (double x1,double y1,double x2,double y2,double x3,double y3,double x4,double y4) {
// Detects two lines that cross
//
each other
int a=0;
// Will be 1 if there is a crossing
double angle=Math.atan((y2

y1)/(x2

x1));
// The angle between the first line and the x

axis
double cos=Math.cos(angle);
double sin=Math.sin(angle);
double X2=(x2

x1)*cos+(y2

y1)*sin;
// The
X coordinate of (x2,y2) on an imaginary axes system
double X3=(x3

x1)*cos+(y3

y1)*sin;
// The X coordinate of (x3,y3) on an imaginary axes system
double Y3=(y3

y1)*cos

(x3

x1)*sin;
// The Y coordinate of (x3,y3) on an imaginary axes system
double X
4=(x4

x1)*cos+(y4

y1)*sin;
// The X coordinate of (x4,y4) on an imaginary axes system
double Y4=(y4

y1)*cos

(x4

x1)*sin;
// The Y coordinate of (x4,y4) on an imaginary axes system
if ((Y3==0) && (Y4==0)) {
// The lines may coincide
if (X2>0) {
if (((X3<X2) && (X4>=X2))  ((X4<X2) && (X3>=X2))) {
// X2 is between them
a=1;
}
else if (((X3<=0) && (X4>0))  ((X4<=0) && (X3>0))) {
// X1(=0) is between them
a=1;
}
else if (((X3<=0) && (X4>=X2))  ((X4<=0) && (X3>=X2)))
{
// First line contained in the second
a=1;
}
else if ((X3>0) && (X4<X2)) {
// Second line contained in the first
a=1;
}
}
else {
// X2 is negative
if (((X3<=X2) && (X4>X2))  ((X4<=X2) && (X3>X2))) {
// X2 is betwe
en them
a=1;
}
else if (((X3<0) && (X4>=0))  ((X4<0) && (X3>=0))) {
// X1(=0) is between them
a=1;
}
else if (((X3<=X2) && (X4>=0))  ((X4<=X2) && (X3>=0))) {
// First line contained in the second
a=1;
}
else if (
(X3>X2) && (X4<0)) {
// Second line contained in the first
a=1;
}
}
}
else {
double X=X3

Y3*((X4

X3)/(Y4

Y3));
// The solution of the equation of the second line for Y=0 on the imaginary axes system
if (((Y3>=0) && (Y4<=0))  ((Y
3<=0) && (Y4>=0))) {
// Only if the second line crosses the imaginary X

axis...
if (X2>0) {
if ((X>=0) && (X<=X2)) {
// ...and only if X is between X1(=0) and X2, will the lines cross each other
a=1;
}
}
else {
if ((X<=0) &
& (X>=X2)) {
a=1;
}
}
}
}
return a;
}
public int FindCrossings () {
// Checks the current route for crossings
int a=0;
// Total number of crossings
for (int counter=0;counter<=N

3;counter++) {
// The last two will have already
been checked
if (counter==0) {
// Prevents the check of the first line with the last line...
for (int counter2=counter+2;counter2<=N

2;counter2++) {
// ...by going only up to N

2
a=a+DetectCrossing(x[route[counter]],y[route[counter]],x[route[coun
ter+1]],y[route[counter+1]],x[route[counter2]],y[route[counter2]],x[route[counter2+1]],y[route[counter2+1]]);
}
}
else {
for (int counter2=counter+2;counter2<=N

1;counter2++) {
// Starts from +2, the first line that possibly crosses the curre
nt
a=a+DetectCrossing(x[route[counter]],y[route[counter]],x[route[counter+1]],y[route[counter+1]],x[route[counter2]],y[route[cou
nter2]],x[route[counter2+1]],y[route[counter2+1]]);
}
}
}
return a;
}
public void paint (Graphics g) {
int Dis
tance=0;
// Total distance for initial route
int Distnew=0;
// Will be used during the search for a better route
int Distold=0;
// Will be replaced by Distnew if that is less
int crossings=1;
// Number of crossings of paths, the algorithms will mak
e it zero
double T=100;
// The basis of the simulated annealing algorithm
route[N]=0;
// The last city to be visited will be the home
g.drawRect(35,140,310,310);
// Map 1

Initial Route
g.drawRect(425,140,310,310);
// Map 2

Final Route
g.dr
awString("INITIAL ROUTE",150,125);
g.drawString("FINAL ROUTE",550,125);
for (int counter=0;counter<=N

1;counter++) {
// Draws the maps
if (check1.getState()!=true) {
// Keeps the same map if check1 is true
x[counter]=((int)(Math.random()*10
1))*3+40;
// Multiplied by 3 to magnify the map
y[counter]=((int)(Math.random()*101))*3+145;
for (int counter2=0;counter2<=counter

1;counter2++) {
// Checks all the previous cities up to the current

1
if ((x[counter]==x[counter2]) && (y[counte
r]==y[counter2])) {
// If one is the same...
counter=counter

1;
// ...repeats the last step of the previous loop and...
counter2=counter;
// ...exits this loop
}
}
}
Circle(g,x[counter],y[counter],5);
// The cities drawn on map
1
Circle(g,x[counter]+390,y[counter],5);
// The cities drawn on map 2
if (check2.getState()==true) {
// Displays the numbers next to the cities
g.setColor(Color.magenta);
g.drawString(""+(counter+1),x[counter]+15,y[counter]+5);
g.drawSt
ring(""+(counter+1),x[counter]+405,y[counter]+5);
g.setColor(Color.black);
}
route[counter]=counter;
// Initial route will be 0,1,2,...,N

1,0
}
DisplayRoute(g,0);
Distance=CalculateTotalDistance();
Distold=Distance;
g.drawString("Total
distance travelled: "+Distance,130,470);
if (algorithm=="bruteforce") {
/*****
*****
***** ALGORITHM 1: BRUTE FORCE **
*****
********/
while (crossings!=0) {
// Will only stop when an acceptable route is found (no crossings of paths)
for (int coun
ter=0;counter<=N

1;counter++) {
// Resets the route before trying again
route[counter]=counter;
}
Distold=Distance;
// Resets Distold
for (int counter=1;counter<=10000;counter++) {
int a=(int)(Math.random()*(N

1))+1;
// Will be used
to swap the order of 2 random cities...
int b=a;
while (b==a) {
// ...ensuring that they are not the same
b=(int)(Math.random()*(N

1))+1;
}
int previous_route_a=route[a];
int previous_route_b=route[b];
route[a]=route[b]
;
route[b]=previous_route_a;
Distnew=CalculateTotalDistance();
if (Distnew<=Distold) {
// If Distnew is an improvement, replace Distold
Distold=Distnew;
}
else {
// Try again from previous route
route[a]=previous_rout
e_a;
route[b]=previous_route_b;
}
}
crossings=FindCrossings();
}
if (BFexecutions<100) {
// Stores the current percentage of improvement
BFimprovement[BFexecutions]=(int)Math.round(((double)(Distance

Distold)/(double)Distance)*
100);
BFexecutions++;
}
total1=0;
for (int counter=0;counter<=99;counter++) {
total1=total1+BFimprovement[counter];
// Adds all the elements together
}
}
if (algorithm=="annealing") {
/***
*****
*** ALGORITHM 2: SIMULATED ANNEALING
**
*****
****/
while (crossings!=0) {
// Will only stop when an acceptable route is found (no crossings of paths)
T=100;
// Resets T
for (int counter=0;counter<=N

1;counter++) {
// Resets the route
route[counter]=counter;
}
Distold
=Distance;
// Resets Distold
while (T>0.1) {
for (int counter=1;counter<=30;counter++) {
int a=(int)(Math.random()*(N

1))+1;
// Will be used to swap the order of 2 random cities...
int b=a;
while (b==a) {
// ...ensuring that th
ey are not the same
b=(int)(Math.random()*(N

1))+1;
}
int previous_route_a=route[a];
int previous_route_b=route[b];
route[a]=route[b];
route[b]=previous_route_a;
Distnew=CalculateTotalDistance();
if (Distnew
<=Distold) {
// If Distnew is an improvement, replace Distold
Distold=Distnew;
}
else {
// There is still a chance Distold will be replaced
double probability=Math.pow(Math.E,

(double)(Distnew

Distold)/T);
// A number in t
he range [0,1)
double dice=Math.random();
// Needed to use the probability
if (dice<=probability) {
Distold=Distnew;
}
else {
// Try again from previous route
route[a]=previous_route_a;
route[b]=previou
s_route_b;
}
}
}
T=0.99*T;
}
crossings=FindCrossings();
}
if (SAexecutions<100) {
// Stores the current percentage of improvement
SAimprovement[SAexecutions]=(int)Math.round(((double)(Distance

Distold)/(double)Dista
nce)*100);
SAexecutions++;
}
total2=0;
for (int counter=0;counter<=99;counter++) {
total2=total2+SAimprovement[counter];
// Adds all the elements together
}
}
DisplayRoute(g,390);
// The final route is shown on the second map
g.
drawString("New total distance travelled: "+Distold,505,470);
if (N!=0) {
// Home is repainted blue on the maps
g.drawString("Home",60,470);
g.drawRect(35,450,70,30);
g.setColor(Color.blue);
Circle(g,x[0],y[0],5);
Circle(g,x[0]+390,y[0],5
);
Circle(g,50,465,5);
g.setColor(Color.black);
}
g.drawString("Mean value of improvement percentage:",80,520);
if (BFexecutions!=0) {
g.drawString("Brute Force Algorithm: "+Math.round((double)total1/(double)BFexecutions),370,510);
// The m
ean value
g.drawString(" % (Current: "+BFimprovement[BFexecutions

1]+" %)",508,510);
// The current improvement percentage
}
else {
g.drawString("Brute Force Algorithm: 0 % (Current: 0 %)",370,510);
}
if (SAexecutions!=0) {
g.drawStr
ing("Simulated Annealing Algorithm: "+Math.round((double)total2/(double)SAexecutions),318,530);
// The mean value
g.drawString(" % (Current: "+SAimprovement[SAexecutions

1]+" %)",508,530);
// The current improvement percentage
}
else {
g.
drawString("Simulated Annealing Algorithm: 0 % (Current: 0 %)",318,530);
}
}
}
Comments 0
Log in to post a comment