Sunteți pe pagina 1din 23

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

Procedure
1. Beginning of code Variable declaration The first three lines import the standard libraries, needed for creating an 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 program. 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 algorithms 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 BFimprovement[ ] or SAimprovement[ ], depending on the algorithm. The BFexecutions 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 BFexecutions 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 background. 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 button 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, options and the Start button. 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 instruct 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 Graphical 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 necessary. 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 number. 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 circle. 5. drawArrow method Drawing arrows along the route This method will connect 2 cities by drawing an arrow, which shows the order in which these cities are visited. It also helps to make out the route through a group of cities that happened to be placed 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 equivalent angle. The tangent of the angle, which any line between two points, (x1,y1) y2 y1 and (x2,y2), forms with the x-axis, is given by . The results of the subtractions are converted x2 x1 to doubles because a division between integers would return an integer. If the line is vertical to the xaxis, then the tangent is equal to infinity, which means that the angle is 90o. The sine and the cosine of the angle are then calculated. The next step involves two independent changes in the axes system,

to 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. y Y size/2 City 1 City 2 (x4,y4) (x3,y3) [(x2,y2),(X2,0)] X

r (x5,y5) angle [(x1,y1),(0,0)] 0 Figure 1. The drawArrow method size

The first change is called 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 the 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 calculations 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 in (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: X2=(x2-x1)*cos(angle)+(y2-y1)*sin(angle) A this point, the 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 line 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. The fillPolygon method can now make use of the three points to create the red arrowhead.

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 theorem. 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 method 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 method 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 distance 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. DetectCrossing 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 necessary 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 doesnt matter what the real coordinates of that point are. The 8 variables (2 for each point) are converted 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. y Y City 3 [(x3,y3),(X3,Y3)] [(x2,y2),(X2,0)] X

(X,0)
City 2 City 1 Equation: Y=0 [(x4,y4),(X4,Y4)] angle [(x1,y1),(0,0)] Equation: Y=((Y4-Y3)/(X4-X3))*(X-X3)+Y3 0 Figure 2 The DetectCrossing method x 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), then the line doesnt 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: X4 X3 X X3 Y3 * Y4 Y3 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 method 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 line 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 the 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 dont 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 home. Two rectangle areas, which will represent the maps, are then drawn to the screen and a name for each 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 whole 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 are 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 center. The radius of the circle is 5. By increasing the x-coordinates, a mirror of the first map is shown on the second rectangle. Depending on the 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 that, starting and finishing at 0 (the home), the salesman visits all the cities in the order in which they were created. Once the loop finishes, the DisplayRoute method connects all the cities together according to the initial route. The CalculateTotalDistance method is called next and it returns the total distance traveled by the salesman, which is saved in both Distance and Distold.

Brute Force Algorithm

If the selected 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 that will stop repeating the algorithm when the FindCrossings method reports that it did not find any crossings. The route[ ] and Distold variables are 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 the cities in the respective positions of the route are visited. This means that the entries route[a] and route[b] swap their value, but only after their previous value is stored in previous_route_a and previous_route_b. Once the new route has been defined, the CalculateTotalDistance method is called again 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 isnt shorter, then the route is restored to its previous state. 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 the new route or not. If there are, the while loop will repeat the whole algorithm 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 result 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 based in the same principle as the Brute Force algorithm and it uses the T variable, which is reset 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 value in the exact same range. If dice is less than or equal to the probability, then Distnew replaces Distold, 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 same 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 30 cities, using Simulated 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 represents 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 SAimprovement[ ] is displayed under the maps, together with the last percentage of improvement that each algorithm produced. This information is updated every time the 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 to the TravellingSalesmanProblem.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 Applet Viewer

Testing the program - Discussion


White Box Tests

The procedure called whitebox testing took place during and after the coding 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 working 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 possible 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 corrected 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 incremented 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. Using 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 whole 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 Simulated 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 pressed, 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 interest at this stage. The program has to be compared with the instructions/specifications sheet, which was given before the writing of the code. By pressing 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:
Improvement results for 10 cities
49% 48% 48% 47% 47% 46% 46% 45% 1 2 3 4 5 6 7 8 9 10 Brute Force Simulated Annealing Tests on the same map

Improvement results for 20 cities


62% 60% 58% 56% 54% 52% 50% 48% 46% 44% 1 2 3 4 5 6 7 8 9 10 Brute Force Simulated Annealing Tests on the same map

Improvement results for 30 cities


72% 70% 68% 66% 64% 62% 60% 58% 56% 1 2 3 4 5 6 7 8 9 10 Brute Force Simulated Annealing Tests on the same map

Improvement results for 50 cities


78% 76% 74% 72% 70% 68% 66% 64% 62% 60% 58% 1 2 3 4 5 6 7 8 9 10 Brute Force Simulated Annealing Tests on the same map

Time needed to solve the problem of 50 cities


01:40 01:26 01:12 00:57 00:43 00:28 00:14 00:00 1 Brute Force Simulated Annealing 2 3 4 5 6 7 8 9 Tests on different maps

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 nine 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 algorithms 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 maps in the tests were the random maps with 50 cities (example in Picture 5). The routes that were found were a greater improvement from the initial routes, 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 minutes! Unfortunately, a problem which causes the area that the paint method uses to be reloaded whenever another window 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 repeats 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 fewer 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. Time 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 RAM 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 Annealing 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 would 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. Choosing 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 30,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), meaning that the algorithm would be 17 times slower than Simulated Annealing (at most). For 50 cities, the number has 63 digits and for 172 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), 3rd grade of High school. 6th ed, OEDB, Athens. CEM KANER, JACK FALK, HUNG QUOC NGUYEN (1993). TESTING COMPUTER SOFTWARE. 2nd ed, International Thompson Publishing.

Program listing TravellingSalesmanProblem.java


/* The Travelling Salesman 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(); Label 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 cities Checkbox check3=new Checkbox("Hide arrowheads"); // Ignores the drawArrow method Button button1=new Button("Start"); String algorithm="bruteforce"; int N=10; int x[]=new int[50]; int y[]=new int[50]; int route[]=new int[51]; int BFexecutions=0; int BFimprovement[]=new int[100]; int total1=0; // The type of algorithm to be used, "bruteforce" or "annealing" // Number of cities // x coordinates of the cities // y coordinates of the cities // The order in which the cities are visited // Number of times the brute force algorithm was executed // Stores the percentages of improvement of the brute force algorithm // The sum of the elements of BFimprovement[], helps to find the mean value

int SAexecutions=0; // Number of times the simulated 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 void 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); panel1.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 GridLayout(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); panel4.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) { // Changes 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.getSelectedCheckbox()==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) { g.fillOval(x-r,y-r,2*r,2*r); } public void drawArrow (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 // Represents the cities, r=radius

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 the 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+(int)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)*cos); } 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" controls 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 theorem

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(LineLength(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 X4=(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))) { a=1; } else if (((X3<0) && (X4>=0)) || ((X4<0) && (X3>=0))) { a=1; } else if (((X3<=X2) && (X4>=0)) || ((X4<=X2) && (X3>=0))) { a=1; } else if ((X3>X2) && (X4<0)) { a=1; }

// X2 is between them

// X1(=0) is between them

// First line contained in the second

// Second line contained in the first

} } 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)) || ((Y3<=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++) { } } else {

// ...by going only up to N-2

a=a+DetectCrossing(x[route[counter]],y[route[counter]],x[route[counter+1]],y[route[counter+1]],x[route[counter2]],y[route[counter2]],x[route[counter2+1]],y[route[counter2+1]]);

for (int counter2=counter+2;counter2<=N-1;counter2++) { // Starts from +2, the first line that possibly crosses the current
a=a+DetectCrossing(x[route[counter]],y[route[counter]],x[route[counter+1]],y[route[counter+1]],x[route[counter2]],y[route[counter2]],x[route[counter2+1]],y[route[counter2+1]]);

} } } return a; } public void paint (Graphics g) { int Distance=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 make 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.drawString("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()*101))*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[counter]==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.drawString(""+(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 counter=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_route_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 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 { // There is still a chance Distold will be replaced double probability=Math.pow(Math.E,-(double)(Distnew-Distold)/T); // A number in the 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]=previous_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)Distance)*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 mean 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.drawString("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); } } }

S-ar putea să vă placă și