Travelling Salesman Problem in Java

Λογισμικό & κατασκευή λογ/κού

23 Ιουλ 2011 (πριν από 8 χρόνια και 7 μέρες)

6.739 εμφανίσεις

Click here to run the application (Java applet): http://www.kosvl.pwp.blueyonder.co.uk/files/travelling_salesman/applet.html The aim of this assignment is to create a program in Java, which will solve the traveling salesman problem. The definition 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.

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

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
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
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

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
).

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
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
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

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

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();

Label label2=new
Label("Choose the number of cities:");

Panel panel3=new Panel();

CheckboxGroup group2=new CheckboxGroup();

;

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

// 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);

c.insets=new Insets(0,0,0,20);

c.gridx=1;

c.gridy=0;

gridbag.setConstraints(panel2,c);

panel2.setLayout(new GridLayout(2,1));

panel

c.insets=new Insets(0,0,0,0);

c.gridx=3;

c.gridy=0;

gridbag.setConstraints(label2,c);

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));

c.gridx=5;

c.gridy=0;

gridbag.setConstraints(panel4,c);

panel4.setLayout(new GridLayout(3,1));

panel

c.insets=new Insets(0,0,0,5);

c.gridx=6;

c.gridy=0;

gridbag.setConstraints(button1,c);

;

}

public void actionPerformed (ActionEvent e) {

// The Start button restarts the paint method

algorithm="bruteforce";

}

else {

algorithm="annealing";

}

if (check1.getState()!=true) {

// Cha
nges the number of cities if check1 is false

N=10;

}

N=20;

}

N=30;

}

if (group2.getSelected

N=50;

}

}

else {

// Returns the number of cities to the locked selection

if (N==10) {

}

if (N==20) {

}

if (N==30) {

group2.

}

if (N==50) {

}

}

repaint();

}

public void Circle (Graphics g,int x,int y,int r) {

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;

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);

}

}

}