├── README.md ├── LICENSE ├── examples ├── a280.txt ├── ch130.txt └── ch150.txt ├── VNS.py ├── SA.py └── GA.py /README.md: -------------------------------------------------------------------------------- 1 | # TSP-Solver 2 | 3 | Solve TSP problem using GA, VNS, SA 4 | 5 | 人工智能实验作业,用遗传算法、变邻域搜索、退火算法解决旅行商问题。 6 | 7 | example文件夹中存放三个城市数大于100的测例。更多测例来源[TSPLIB](http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/)。 8 | 9 | ### VNS.py 10 | 11 | 变邻域策略的局部搜索算法 12 | 13 | ### SA.py 14 | 15 | 在变邻域搜索的基础上,加入了模拟退火策略的局部搜索算法 16 | 17 | ### GA.py 18 | 19 | 遗传算法 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 JohnnyassSilverhand 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/a280.txt: -------------------------------------------------------------------------------- 1 | NAME : a280 2 | TYPE: TSP 3 | COMMENT: 280 drilling problem (Ludwig) 4 | DIMENSION: 280 5 | EDGE_WEIGHT_TYPE: EUC_2D 6 | NODE_COORD_SECTION 7 | 1 288 149 8 | 2 288 129 9 | 3 270 133 10 | 4 256 141 11 | 5 256 157 12 | 6 246 157 13 | 7 236 169 14 | 8 228 169 15 | 9 228 161 16 | 10 220 169 17 | 11 212 169 18 | 12 204 169 19 | 13 196 169 20 | 14 188 169 21 | 15 196 161 22 | 16 188 145 23 | 17 172 145 24 | 18 164 145 25 | 19 156 145 26 | 20 148 145 27 | 21 140 145 28 | 22 148 169 29 | 23 164 169 30 | 24 172 169 31 | 25 156 169 32 | 26 140 169 33 | 27 132 169 34 | 28 124 169 35 | 29 116 161 36 | 30 104 153 37 | 31 104 161 38 | 32 104 169 39 | 33 90 165 40 | 34 80 157 41 | 35 64 157 42 | 36 64 165 43 | 37 56 169 44 | 38 56 161 45 | 39 56 153 46 | 40 56 145 47 | 41 56 137 48 | 42 56 129 49 | 43 56 121 50 | 44 40 121 51 | 45 40 129 52 | 46 40 137 53 | 47 40 145 54 | 48 40 153 55 | 49 40 161 56 | 50 40 169 57 | 51 32 169 58 | 52 32 161 59 | 53 32 153 60 | 54 32 145 61 | 55 32 137 62 | 56 32 129 63 | 57 32 121 64 | 58 32 113 65 | 59 40 113 66 | 60 56 113 67 | 61 56 105 68 | 62 48 99 69 | 63 40 99 70 | 64 32 97 71 | 65 32 89 72 | 66 24 89 73 | 67 16 97 74 | 68 16 109 75 | 69 8 109 76 | 70 8 97 77 | 71 8 89 78 | 72 8 81 79 | 73 8 73 80 | 74 8 65 81 | 75 8 57 82 | 76 16 57 83 | 77 8 49 84 | 78 8 41 85 | 79 24 45 86 | 80 32 41 87 | 81 32 49 88 | 82 32 57 89 | 83 32 65 90 | 84 32 73 91 | 85 32 81 92 | 86 40 83 93 | 87 40 73 94 | 88 40 63 95 | 89 40 51 96 | 90 44 43 97 | 91 44 35 98 | 92 44 27 99 | 93 32 25 100 | 94 24 25 101 | 95 16 25 102 | 96 16 17 103 | 97 24 17 104 | 98 32 17 105 | 99 44 11 106 | 100 56 9 107 | 101 56 17 108 | 102 56 25 109 | 103 56 33 110 | 104 56 41 111 | 105 64 41 112 | 106 72 41 113 | 107 72 49 114 | 108 56 49 115 | 109 48 51 116 | 110 56 57 117 | 111 56 65 118 | 112 48 63 119 | 113 48 73 120 | 114 56 73 121 | 115 56 81 122 | 116 48 83 123 | 117 56 89 124 | 118 56 97 125 | 119 104 97 126 | 120 104 105 127 | 121 104 113 128 | 122 104 121 129 | 123 104 129 130 | 124 104 137 131 | 125 104 145 132 | 126 116 145 133 | 127 124 145 134 | 128 132 145 135 | 129 132 137 136 | 130 140 137 137 | 131 148 137 138 | 132 156 137 139 | 133 164 137 140 | 134 172 125 141 | 135 172 117 142 | 136 172 109 143 | 137 172 101 144 | 138 172 93 145 | 139 172 85 146 | 140 180 85 147 | 141 180 77 148 | 142 180 69 149 | 143 180 61 150 | 144 180 53 151 | 145 172 53 152 | 146 172 61 153 | 147 172 69 154 | 148 172 77 155 | 149 164 81 156 | 150 148 85 157 | 151 124 85 158 | 152 124 93 159 | 153 124 109 160 | 154 124 125 161 | 155 124 117 162 | 156 124 101 163 | 157 104 89 164 | 158 104 81 165 | 159 104 73 166 | 160 104 65 167 | 161 104 49 168 | 162 104 41 169 | 163 104 33 170 | 164 104 25 171 | 165 104 17 172 | 166 92 9 173 | 167 80 9 174 | 168 72 9 175 | 169 64 21 176 | 170 72 25 177 | 171 80 25 178 | 172 80 25 179 | 173 80 41 180 | 174 88 49 181 | 175 104 57 182 | 176 124 69 183 | 177 124 77 184 | 178 132 81 185 | 179 140 65 186 | 180 132 61 187 | 181 124 61 188 | 182 124 53 189 | 183 124 45 190 | 184 124 37 191 | 185 124 29 192 | 186 132 21 193 | 187 124 21 194 | 188 120 9 195 | 189 128 9 196 | 190 136 9 197 | 191 148 9 198 | 192 162 9 199 | 193 156 25 200 | 194 172 21 201 | 195 180 21 202 | 196 180 29 203 | 197 172 29 204 | 198 172 37 205 | 199 172 45 206 | 200 180 45 207 | 201 180 37 208 | 202 188 41 209 | 203 196 49 210 | 204 204 57 211 | 205 212 65 212 | 206 220 73 213 | 207 228 69 214 | 208 228 77 215 | 209 236 77 216 | 210 236 69 217 | 211 236 61 218 | 212 228 61 219 | 213 228 53 220 | 214 236 53 221 | 215 236 45 222 | 216 228 45 223 | 217 228 37 224 | 218 236 37 225 | 219 236 29 226 | 220 228 29 227 | 221 228 21 228 | 222 236 21 229 | 223 252 21 230 | 224 260 29 231 | 225 260 37 232 | 226 260 45 233 | 227 260 53 234 | 228 260 61 235 | 229 260 69 236 | 230 260 77 237 | 231 276 77 238 | 232 276 69 239 | 233 276 61 240 | 234 276 53 241 | 235 284 53 242 | 236 284 61 243 | 237 284 69 244 | 238 284 77 245 | 239 284 85 246 | 240 284 93 247 | 241 284 101 248 | 242 288 109 249 | 243 280 109 250 | 244 276 101 251 | 245 276 93 252 | 246 276 85 253 | 247 268 97 254 | 248 260 109 255 | 249 252 101 256 | 250 260 93 257 | 251 260 85 258 | 252 236 85 259 | 253 228 85 260 | 254 228 93 261 | 255 236 93 262 | 256 236 101 263 | 257 228 101 264 | 258 228 109 265 | 259 228 117 266 | 260 228 125 267 | 261 220 125 268 | 262 212 117 269 | 263 204 109 270 | 264 196 101 271 | 265 188 93 272 | 266 180 93 273 | 267 180 101 274 | 268 180 109 275 | 269 180 117 276 | 270 180 125 277 | 271 196 145 278 | 272 204 145 279 | 273 212 145 280 | 274 220 145 281 | 275 228 145 282 | 276 236 145 283 | 277 246 141 284 | 278 252 125 285 | 279 260 129 286 | 280 280 133 287 | EOF -------------------------------------------------------------------------------- /examples/ch130.txt: -------------------------------------------------------------------------------- 1 | NAME: ch130 2 | TYPE: TSP 3 | COMMENT: 130 city problem (Churritz) 4 | DIMENSION: 130 5 | EDGE_WEIGHT_TYPE: EUC_2D 6 | NODE_COORD_SECTION 7 | 1 334.5909245845 161.7809319139 8 | 2 397.6446634067 262.8165330708 9 | 3 503.8741827107 172.8741151168 10 | 4 444.0479403502 384.6491809647 11 | 5 311.6137146746 2.0091699828 12 | 6 662.8551011379 549.2301263653 13 | 7 40.0979030612 187.2375430791 14 | 8 526.8941409181 215.7079092185 15 | 9 209.1887938487 691.0262291948 16 | 10 683.2674131973 414.2096286906 17 | 11 280.7494438748 5.9206392047 18 | 12 252.7493090080 535.7430385019 19 | 13 698.7850451923 348.4413729766 20 | 14 678.7574678104 410.7256424438 21 | 15 220.0041131179 409.1225812873 22 | 16 355.1528556851 76.3912076444 23 | 17 296.9724227786 313.1312792361 24 | 18 504.5154071733 240.8866564499 25 | 19 224.1079496785 358.4872228907 26 | 20 470.6801296968 309.6259188406 27 | 21 554.2530513223 279.4242466521 28 | 22 567.6332684419 352.7162027273 29 | 23 599.0532671093 361.0948690386 30 | 24 240.5232959211 430.6036007844 31 | 25 32.0825972787 345.8551009775 32 | 26 91.0538736891 148.7213270256 33 | 27 248.2179894723 343.9528017384 34 | 28 488.8909044347 3.6122311393 35 | 29 206.0467939820 437.7639406167 36 | 30 575.8409415632 141.9670960195 37 | 31 282.6089948164 329.4183805862 38 | 32 27.6581484868 424.7684581747 39 | 33 568.5737309870 287.0975660546 40 | 34 269.4638933331 295.9464636385 41 | 35 417.8004856811 341.2596589955 42 | 36 32.1680938737 448.8998721172 43 | 37 561.4775136009 357.3543930067 44 | 38 342.9482167470 492.3321423839 45 | 39 399.6752075383 156.8435035519 46 | 40 571.7371050025 375.7575350833 47 | 41 370.7559842751 151.9060751898 48 | 42 509.7093253204 435.7975189314 49 | 43 177.0206999750 295.6044772584 50 | 44 526.1674198605 409.4859418161 51 | 45 316.5725171854 65.6400108214 52 | 46 469.2908100279 281.9891445025 53 | 47 572.7630641427 373.3208821255 54 | 48 29.5176994283 330.0382309000 55 | 49 454.0082936692 537.2178547659 56 | 50 416.1546762271 227.6133100741 57 | 51 535.2514330806 471.0648643744 58 | 52 265.4455533675 684.9987192464 59 | 53 478.0542110167 509.6452028741 60 | 54 370.4781203413 332.5390063041 61 | 55 598.3479202004 446.8693279856 62 | 56 201.1521139175 649.0260268945 63 | 57 193.6925360026 680.2322840744 64 | 58 448.5792598859 532.7934059740 65 | 59 603.2853485624 134.4006473609 66 | 60 543.0102490781 481.5168231148 67 | 61 214.5750793346 43.6460117543 68 | 62 426.3501451825 61.7285415996 69 | 63 89.0447037063 277.1158385868 70 | 64 84.4920100219 31.8474816424 71 | 65 220.0468614154 623.0778103080 72 | 66 688.4613313444 0.4702312726 73 | 67 687.2857531630 373.5346236130 74 | 68 75.4934933967 312.9175377486 75 | 69 63.4170993511 23.7039309674 76 | 70 97.9363495877 211.0910930878 77 | 71 399.5255884970 170.8221968365 78 | 72 456.3167017346 597.1937161677 79 | 73 319.8855102422 626.8396604886 80 | 74 295.9250894897 664.6291554845 81 | 75 288.4868857235 667.7284070537 82 | 76 268.3951858954 52.9010181645 83 | 77 140.4709056068 513.5566720960 84 | 78 689.8079027159 167.5947003748 85 | 79 280.5784506848 458.7533546925 86 | 80 453.3884433554 282.9082328989 87 | 81 213.5704943432 525.8681817779 88 | 82 133.6953004520 677.1757808026 89 | 83 521.1658690522 132.8617086506 90 | 84 30.2657946347 450.0754502986 91 | 85 657.0199585283 39.7772908299 92 | 86 6.9252241961 23.8749241575 93 | 87 252.4286967767 535.1659364856 94 | 88 42.8551682504 63.8232081774 95 | 89 145.8999393902 399.5255884970 96 | 90 638.4885715591 62.6262558472 97 | 91 489.2756391122 665.3131282446 98 | 92 361.2231139311 564.2347787901 99 | 93 519.9475425732 347.9711417040 100 | 94 129.3349741063 435.6692740389 101 | 95 259.7172815016 454.6495181318 102 | 96 676.3421890013 371.0979706551 103 | 97 84.5133841706 183.3260738572 104 | 98 77.7164048671 354.3833863300 105 | 99 335.9802442534 660.6321896676 106 | 100 264.3554717810 377.5743377274 107 | 101 51.6826916855 676.0429509187 108 | 102 692.1376849300 543.8010925819 109 | 103 169.2191356800 547.8194325476 110 | 104 194.0131482339 263.4791316822 111 | 105 415.1928395332 78.9133571973 112 | 106 415.0432204919 479.0801701569 113 | 107 169.8389859939 245.6103433244 114 | 108 525.0987124228 213.5063718969 115 | 109 238.6851191283 33.4932910965 116 | 110 116.2112467718 363.5742702940 117 | 111 16.9283258126 656.5711014044 118 | 112 434.3440768162 92.6996831431 119 | 113 40.5253860363 424.6829615797 120 | 114 530.4849979086 183.8390534273 121 | 115 484.3595848990 49.2460387276 122 | 116 263.6501248722 426.5852608187 123 | 117 450.2891917862 126.3853415784 124 | 118 441.7822805823 299.7724362653 125 | 119 24.2169105375 500.3474481664 126 | 120 503.7886861157 514.6895019799 127 | 121 635.5389390312 200.9811207275 128 | 122 614.5922732529 418.8691931188 129 | 123 21.7161351334 660.9741760476 130 | 124 143.8266469611 92.6996831431 131 | 125 637.7191022040 54.2048412384 132 | 126 566.5645610042 199.9551615873 133 | 127 196.6849168280 221.8209157619 134 | 128 384.9270448985 87.4630166986 135 | 129 178.1107815614 104.6905805938 136 | 130 403.2874386776 205.8971749407 137 | EOF -------------------------------------------------------------------------------- /examples/ch150.txt: -------------------------------------------------------------------------------- 1 | NAME: ch150 2 | TYPE: TSP 3 | COMMENT: 150 city Problem (churritz) 4 | DIMENSION: 150 5 | EDGE_WEIGHT_TYPE: EUC_2D 6 | NODE_COORD_SECTION 7 | 1 37.4393516691 541.2090699418 8 | 2 612.1759508571 494.3166877396 9 | 3 38.1312338227 353.1484581781 10 | 4 53.4418081065 131.4849013650 11 | 5 143.0606355347 631.7200953923 12 | 6 689.9451267256 468.5354998742 13 | 7 112.7478815786 529.4177578260 14 | 8 141.4875865042 504.8184855710 15 | 9 661.0513901702 445.9375182115 16 | 10 98.7899036592 384.5926031158 17 | 11 697.3881696597 180.3962284275 18 | 12 536.4894189738 287.2279085051 19 | 13 192.4067320507 20.4394059310 20 | 14 282.7865258765 229.8001556189 21 | 15 240.8251726391 281.5141437200 22 | 16 246.9281323057 322.4613321160 23 | 17 649.7313216456 62.3331575282 24 | 18 352.9658562600 666.7873101942 25 | 19 633.3923676580 534.9398453712 26 | 20 488.3117994040 437.4869439948 27 | 21 141.4039286509 228.4325551488 28 | 22 17.3632612602 240.2407068508 29 | 23 397.5586451389 231.3591208928 30 | 24 565.7853781464 282.3858748974 31 | 25 475.8975387047 468.5392706317 32 | 26 322.4224566559 550.3165478233 33 | 27 397.5586634023 74.7588387765 34 | 28 672.8618339396 432.8826409630 35 | 29 571.2189680147 530.2616991530 36 | 30 104.6531165914 482.8224768783 37 | 31 356.7098388794 67.6477131712 38 | 32 400.4070255527 253.6794479997 39 | 33 282.3036243109 426.8380500923 40 | 34 58.7766988363 507.1712386832 41 | 35 189.7506224400 460.3815233617 42 | 36 659.9124120147 226.6284156239 43 | 37 639.0307636033 467.2302300719 44 | 38 415.0258357432 233.3045376118 45 | 39 547.2662016307 161.6589278401 46 | 40 616.6547902644 339.3409309407 47 | 41 494.8592427417 148.1217856389 48 | 42 629.9980812186 433.4548164038 49 | 43 471.1014312410 314.2219307579 50 | 44 138.2440514421 137.1679919735 51 | 45 91.5847556724 110.0203007516 52 | 46 390.6972811808 423.9774318385 53 | 47 565.1617825137 429.1598152874 54 | 48 54.5248980387 438.5515408431 55 | 49 334.3508329710 153.7969238040 56 | 50 531.0291024509 612.3874827889 57 | 51 475.7345905802 385.7844618897 58 | 52 228.8325218994 410.4461939615 59 | 53 578.3805347586 321.3303494537 60 | 54 358.9170574485 404.4670352898 61 | 55 486.4648554867 593.0429937016 62 | 56 343.1693707670 509.3123571315 63 | 57 530.3626972076 137.6881275684 64 | 58 498.8065475299 576.2102674608 65 | 59 224.3182715500 312.4677490415 66 | 60 595.8360732590 81.8130051356 67 | 61 661.5588724308 217.0456944477 68 | 62 43.6892045516 305.4722789165 69 | 63 79.4653452530 445.9641737689 70 | 64 210.4163247004 130.7151137038 71 | 65 432.2642292251 629.4092661116 72 | 66 623.2487161301 69.1892850840 73 | 67 436.5194739944 282.9356456070 74 | 68 59.4163265482 40.1280234442 75 | 69 630.9230074073 230.3429888130 76 | 70 579.3265539688 601.0359410602 77 | 71 117.8624507480 112.9796833705 78 | 72 297.7912565664 166.3131886803 79 | 73 22.7642703744 455.5340094037 80 | 74 259.7095810385 10.6199925885 81 | 75 342.3579873647 599.3880182608 82 | 76 10.0260950143 488.9310558282 83 | 77 315.2926064118 273.2275475579 84 | 78 220.7044919297 270.0819745721 85 | 79 192.1186059948 314.1839922798 86 | 80 271.5042718992 225.2921989972 87 | 81 530.7320005441 504.0670155337 88 | 82 42.5331441666 656.3645162886 89 | 83 396.1274792588 539.4648066027 90 | 84 118.6631474021 508.7129103929 91 | 85 395.6913876595 699.5376048429 92 | 86 559.0157105844 560.8866941411 93 | 87 22.6471035906 526.2470392816 94 | 88 135.6377085256 325.8409901555 95 | 89 141.4507014379 485.2477927763 96 | 90 396.7741299332 460.7557115283 97 | 91 87.7494562765 19.6170129082 98 | 92 350.4245639661 420.6531186835 99 | 93 216.7010817133 466.4816410995 100 | 94 130.9237737024 351.1491733079 101 | 95 72.6329856671 645.7852219213 102 | 96 144.6002949996 457.4224283926 103 | 97 212.3725077442 594.9216893413 104 | 98 49.9186786455 541.4350825349 105 | 99 656.6943525585 558.1109593509 106 | 100 176.5941623792 648.5239953299 107 | 101 500.3825200226 198.7428378322 108 | 102 634.3178678420 612.8291643194 109 | 103 59.7537372726 551.6321886765 110 | 104 15.2145765106 143.0441928532 111 | 105 283.0054378872 376.4439530184 112 | 106 146.5389000907 39.4231794338 113 | 107 101.8685605377 635.0986850180 114 | 108 588.1968537448 580.5946976921 115 | 109 457.2628632528 350.0164047376 116 | 110 537.4663680494 472.5842276692 117 | 111 269.3669098585 367.4763636538 118 | 112 239.9045383695 102.6297653390 119 | 113 88.4677500396 384.0507209275 120 | 114 658.9133693395 583.9575181023 121 | 115 97.7359146347 157.4558657632 122 | 116 506.6191384007 233.0022156094 123 | 117 500.2566898239 64.9136393489 124 | 118 594.4048565021 275.8741868990 125 | 119 66.2308146610 24.1317387604 126 | 120 598.4162993909 414.5557574275 127 | 121 172.3088330830 344.3963466366 128 | 122 299.4812851800 251.8295121320 129 | 123 303.8379894831 21.0526063790 130 | 124 197.8969269840 512.3888960980 131 | 125 56.0199567669 243.0663818382 132 | 126 255.5566183121 448.8651882442 133 | 127 608.4256112402 222.5421309272 134 | 128 70.2722703273 77.9227026433 135 | 129 398.2298999899 119.5576573860 136 | 130 635.4970237093 133.3225902609 137 | 131 378.3484559418 272.2907677147 138 | 132 484.8029663388 677.0730379436 139 | 133 278.8710882619 299.9308770828 140 | 134 381.6537300653 360.3337602785 141 | 135 557.6070707573 595.3185092281 142 | 136 249.0589749342 76.6595112599 143 | 137 562.9048787838 670.0382113114 144 | 138 398.5504365580 392.6493259144 145 | 139 590.8939720560 370.7414913742 146 | 140 558.2008003726 0.4198814512 147 | 141 461.4114714423 530.5254969413 148 | 142 354.7242881504 685.4045361900 149 | 143 193.6611295657 669.7432521028 150 | 144 352.3140807211 140.3273323662 151 | 145 308.4345709740 115.2054269847 152 | 146 299.5881370080 530.5889619020 153 | 147 334.2748764383 152.1494569394 154 | 148 690.9658585947 134.5793307203 155 | 149 48.0798124069 270.9680673720 156 | 150 91.6467647724 166.3541158474 157 | EOF -------------------------------------------------------------------------------- /VNS.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import random 3 | import numpy as np 4 | import time 5 | 6 | 7 | #读取城市的x,y坐标 8 | def load(txt): 9 | f = open(txt) 10 | map=[] 11 | flag = 0 12 | for line in f: 13 | line = line.strip() 14 | if line == "NODE_COORD_SECTION": 15 | flag = 1 16 | continue 17 | if line == "EOF": 18 | break 19 | if flag: 20 | a = line.split() 21 | map.append((float(a[1]),float(a[2]))) 22 | return tuple(map) 23 | 24 | #获取两个城市间的二维欧几里得距离 25 | def getDist(): 26 | global map,size 27 | dist = np.zeros((size,size)) 28 | for i in range(0,size): 29 | for j in range(0,size): 30 | dist[i][j] = ((map[i][0]-map[j][0])**2 + (map[i][1]-map[j][1])**2)**0.5 31 | return dist 32 | 33 | txt = "C:\\Users\\Cecilia\\Desktop\\TSP\\a280.txt" 34 | map = load(txt) 35 | size = len(map) 36 | visited = {} 37 | solutions = [] 38 | DIST = getDist() 39 | count = 0 40 | 41 | #根据路径获取该路径总代价 42 | def getCost(path): 43 | cost = 0 44 | former = path[0] 45 | for city in path: 46 | cost += DIST[former][city] 47 | former = city 48 | cost += DIST[path[0]][path[-1]] 49 | return cost 50 | 51 | #扰动产生新的随机解,扰动方式为分成四个区间随机排序 52 | def shaking(path): 53 | global size 54 | ini = visited[path] 55 | cnt = 0 56 | while True: 57 | pos1,pos2,pos3 = sorted(random.sample(range(0,size),3)) 58 | path_ = path[pos1:pos2] + path[:pos1] + path[pos3:] + path[pos2:pos3] 59 | if path_ not in visited: 60 | cost = getCost(path_) 61 | visited.update({path_:cost}) 62 | else: 63 | cost = visited[path_] 64 | cnt+=1 65 | if ini >= cost: 66 | break 67 | elif cnt > 100: 68 | path_ = path 69 | cost = ini 70 | break 71 | return path_ 72 | 73 | 74 | #反转一段区间,获取新邻域 75 | def getNei_rev(path): 76 | global size 77 | min = visited[path] 78 | cnt = 0 79 | while True: 80 | i,j = sorted(random.sample(range(1,size-1),2)) 81 | path_ = path[:i] + path[i:j+1][::-1] + path[j+1:] 82 | if path_ not in visited: 83 | cost = getCost(path_) 84 | visited.update({path_:cost}) 85 | else: 86 | cost = visited[path_] 87 | cnt+=1 88 | if cost < min: 89 | min = cost 90 | break 91 | elif cnt > 1000: 92 | path_ = path 93 | break 94 | return path_,min 95 | 96 | 97 | #交换两个城市,获取新邻域 98 | def getNei_exc(path): 99 | global size 100 | min = visited[path] 101 | cnt = 0 102 | while True: 103 | i,j = sorted(random.sample(range(1,size-1),2)) 104 | path_ = path[:i] + path[j:j+1] + path[i+1:j] + path[i:i+1] + path[j+1:] 105 | if path_ not in visited: 106 | cost = getCost(path_) 107 | visited.update({path_:cost}) 108 | else: 109 | cost = visited[path_] 110 | cnt+=1 111 | if cost < min: 112 | min = cost 113 | break 114 | elif cnt > 1000: 115 | path_ = path 116 | break 117 | return path_,min 118 | 119 | #随机挑选两个城市插入序列头部,获取新邻域 120 | def getNei_ins(path): 121 | global size 122 | min = visited[path] 123 | cnt = 0 124 | while True: 125 | i,j = sorted(random.sample(range(1,size-1),2)) 126 | path_ = path[i:i+1] + path[j:j+1] + path[:i] + path[i+1:j] + path[j+1:] 127 | if path_ not in visited: 128 | cost = getCost(path_) 129 | visited.update({path_:cost}) 130 | else: 131 | cost = visited[path_] 132 | cnt+=1 133 | if cost < min: 134 | min = cost 135 | break 136 | elif cnt > 1000: 137 | path_ = path 138 | break 139 | return path_,min 140 | 141 | #在Local Search中使用VND方法进行搜索 142 | def VND(path): 143 | l = 0 144 | min = visited[path] 145 | while l < 3: 146 | if l == 0: 147 | path_,cost = getNei_rev(path) 148 | elif l == 1: 149 | path_,cost = getNei_exc(path) 150 | elif l == 2: 151 | path_,cost = getNei_ins(path) 152 | if cost < min: 153 | path = path_ 154 | min = cost 155 | l = 0 156 | else: 157 | l+=1 158 | return path,min 159 | 160 | 161 | #进行变邻域局部搜素 162 | def VNS(path,kmax): 163 | k = 0 164 | temp = path 165 | min = solutions[0] 166 | global count 167 | while k < kmax: 168 | #扰动后进行变邻域操作 169 | path_nei,cost = VND(shaking(temp)) 170 | print(cost) 171 | solutions.append(cost) 172 | count+=1 173 | if cost < min: 174 | temp = path_nei #记录迭代过的最优的解 175 | min = cost 176 | k = 0 177 | else: 178 | k+=1 179 | return temp,min 180 | 181 | 182 | 183 | def main(): 184 | time_start = time.time() 185 | global solutions,visited,size,map 186 | kmax = 1000 187 | start = tuple([k for k in range(size)]) 188 | visited.update({start:getCost(start)}) 189 | solutions.append(visited[start]) 190 | path_,cost = VNS(start,kmax) 191 | path = path_[:] + path_[:1] 192 | time_end = time.time() 193 | print() 194 | print('Algorithm VNS iterated',count,'times!\n',sep=' ') 195 | print('It cost ',time_end-time_start,'s',sep='') #此处单位为秒 196 | print('You got the best solution:',cost,sep='\n') 197 | print(path) 198 | best = int(input("The best solution should be: ")) 199 | print("误差为:",(cost-best)/best) 200 | x = np.array([map[i][0] for i in path]) 201 | y = np.array([map[i][1] for i in path]) 202 | i = np.arange(0,len(solutions)) 203 | solutions = np.array(solutions) 204 | plt.subplot(121) 205 | plt.plot(x,y) 206 | plt.subplot(122) 207 | plt.plot(i,solutions) 208 | plt.show() 209 | 210 | 211 | main() 212 | 213 | 214 | -------------------------------------------------------------------------------- /SA.py: -------------------------------------------------------------------------------- 1 | from cmath import log 2 | import matplotlib.pyplot as plt 3 | import random 4 | import numpy as np 5 | import time 6 | import math 7 | 8 | 9 | #读取城市的x,y坐标 10 | def load(txt): 11 | f = open(txt) 12 | map=[] 13 | flag = 0 14 | for line in f: 15 | line = line.strip() 16 | if line == "NODE_COORD_SECTION": 17 | flag = 1 18 | continue 19 | if line == "EOF": 20 | break 21 | if flag: 22 | a = line.split() 23 | map.append((float(a[1]),float(a[2]))) 24 | return tuple(map) 25 | 26 | #获取两个城市间的二维欧几里得距离 27 | def getDist(): 28 | global map,size 29 | dist = np.zeros((size,size)) 30 | for i in range(0,size): 31 | for j in range(0,size): 32 | dist[i][j] = ((map[i][0]-map[j][0])**2 + (map[i][1]-map[j][1])**2)**0.5 33 | return dist 34 | 35 | txt = "C:\\Users\\Cecilia\\Desktop\\TSP\\a280.txt" 36 | map = load(txt) 37 | size = len(map) 38 | visited = {} 39 | solutions = [] 40 | DIST = getDist() 41 | count = 0 42 | 43 | 44 | #根据路径获取该路径总代价 45 | def getCost(path): 46 | cost = 0 47 | former = path[0] 48 | for city in path: 49 | cost += DIST[former][city] 50 | former = city 51 | cost += DIST[path[0]][path[-1]] 52 | return cost 53 | 54 | 55 | #反转一段区间,获取新邻域 56 | def getNei_rev(path): 57 | global size 58 | min = 1000000000 59 | for cnt in range(100): 60 | i,j = sorted(random.sample(range(1,size-1),2)) 61 | path_ = path[:i] + path[i:j+1][::-1] + path[j+1:] 62 | if path_ not in visited: 63 | cost = getCost(path_) 64 | visited.update({path_:cost}) 65 | else: 66 | cost = visited[path_] 67 | if cost < visited[path]: 68 | min = cost 69 | p = path_ 70 | break 71 | if cost < min: 72 | min = cost 73 | p = path_ 74 | '''cost -= DIST[path[i]][path[i-1]] + DIST[path[j]][path[j+1]] 75 | cost += DIST[path[i-1]][path[j]] + DIST[path[i]][path[j+1]] 76 | if int(cost) == int(getCost(path_)): 77 | break 78 | else: 79 | continue''' 80 | return p,min 81 | 82 | 83 | #交换两个城市,获取新邻域 84 | def getNei_exc(path): 85 | global size 86 | min = 1000000000 87 | for cnt in range(100): 88 | i,j = sorted(random.sample(range(1,size-1),2)) 89 | path_ = path[:i] + path[j:j+1] + path[i+1:j] + path[i:i+1] + path[j+1:] 90 | if path_ not in visited: 91 | cost = getCost(path_) 92 | visited.update({path_:cost}) 93 | else: 94 | cost = visited[path_] 95 | if cost < visited[path]: 96 | min = cost 97 | p = path_ 98 | break 99 | if cost < min: 100 | min = cost 101 | p = path_ 102 | '''cost -= DIST[path[i]][path[i-1]] + DIST[path[j]][path[j-1]] + DIST[path[i]][path[i+1]] + DIST[path[j]][path[j+1]] 103 | cost += DIST[path[i-1]][path[j]] + DIST[path[j]][path[i+1]] + DIST[path[j-1]][path[i]] + DIST[path[i]][path[j+1]] 104 | if int(cost) == int(getCost(path_)): 105 | break 106 | else: 107 | continue''' 108 | return p,min 109 | 110 | #随机挑选两个城市插入序列头部,获取新邻域 111 | def getNei_ins(path): 112 | global size 113 | min = 1000000000 114 | for cnt in range(100): 115 | i,j = sorted(random.sample(range(1,size-1),2)) 116 | path_ = path[i:i+1] + path[j:j+1] + path[:i] + path[i+1:j] + path[j+1:] 117 | if path_ not in visited: 118 | cost = getCost(path_) 119 | visited.update({path_:cost}) 120 | else: 121 | cost = visited[path_] 122 | if cost < visited[path]: 123 | min = cost 124 | p = path_ 125 | break 126 | if cost < min: 127 | min = cost 128 | p = path_ 129 | '''cost -= DIST[path[i]][path[i-1]] + DIST[path[j]][path[j-1]] + DIST[path[i]][path[i+1]] + DIST[path[j]][path[j+1]] + DIST[path[0]][path[-1]] 130 | cost += DIST[path[i]][path[j]] + DIST[path[j]][path[0]] + DIST[path[i-1]][path[i+1]] + DIST[path[j-1]][path[j+1]] + DIST[path[-1]][path[i]] 131 | if int(cost) == int(getCost(path_)): 132 | break 133 | else: 134 | continue''' 135 | return p,min 136 | 137 | #在Local Search中使用VND方法进行搜索 138 | def VND(path): 139 | path,min = getNei_rev(path) 140 | l = 1 141 | while l < 3: 142 | if l == 0: 143 | path_,cost = getNei_rev(path) 144 | elif l == 1: 145 | path_,cost = getNei_exc(path) 146 | elif l == 2: 147 | path_,cost = getNei_ins(path) 148 | if cost < min: 149 | path = path_ 150 | min = cost 151 | l = 0 152 | else: 153 | l+=1 154 | return path,min 155 | 156 | #模拟退火算法 157 | def SA(path,kmax,t0,t_end): 158 | temp = path 159 | min = solutions[0] 160 | result = [temp,min] #记录迭代过的最优的解 161 | global count 162 | t = t0 #初始温度 163 | while t > t_end: 164 | for k in range(1,kmax): 165 | path_nei,cost = VND(temp) #进行变邻域操作 166 | #print(cost) 167 | solutions.append(cost) 168 | count+=1 169 | #判断是否接受该解 170 | if cost < min or random.random() < np.exp(-((cost-min)/t*k)): 171 | temp = path_nei 172 | min = cost 173 | if cost < result[1]: 174 | result = [path_nei,cost] 175 | #t/=math.log10(1+k) 176 | t/=k+1 #降温操作 177 | return result[0],result[1] 178 | 179 | 180 | 181 | def main(): 182 | global solutions,visited,size,map 183 | kmax = 100 184 | t0 = 500000 185 | t_end = 0.00001 186 | start = tuple([k for k in range(size)]) 187 | visited.update({start:getCost(start)}) 188 | solutions.append(visited[start]) 189 | time_start = time.time() 190 | global count 191 | count = 0 192 | path_,cost = SA(start,kmax,t0,t_end) 193 | path = path_[:] + path_[:1] 194 | time_end = time.time() 195 | print() 196 | print('Algorithm SA iterated',count,'times!\n',sep=' ') 197 | print('It cost ',time_end-time_start,'s',sep='') #此处单位为秒 198 | print('You got the best solution:',cost,sep='\n') 199 | print(path) 200 | best = 2579 201 | print("误差为:",(cost-best)/best) 202 | x = np.array([map[i][0] for i in path]) 203 | y = np.array([map[i][1] for i in path]) 204 | i = np.arange(0,len(solutions)) 205 | solutions = np.array(solutions) 206 | plt.subplot(121) 207 | plt.plot(x,y) 208 | plt.subplot(122) 209 | plt.plot(i,solutions) 210 | plt.show() 211 | 212 | 213 | main() 214 | 215 | 216 | -------------------------------------------------------------------------------- /GA.py: -------------------------------------------------------------------------------- 1 | from cmath import log 2 | import matplotlib.pyplot as plt 3 | import random 4 | import numpy as np 5 | import time 6 | import math 7 | 8 | from soupsieve import select 9 | 10 | 11 | #读取城市的x,y坐标 12 | def load(txt): 13 | f = open(txt) 14 | map=[] 15 | flag = 0 16 | for line in f: 17 | line = line.strip() 18 | if line == "NODE_COORD_SECTION": 19 | flag = 1 20 | continue 21 | if line == "EOF": 22 | break 23 | if flag: 24 | a = line.split() 25 | map.append((float(a[1]),float(a[2]))) 26 | return tuple(map) 27 | 28 | #获取两个城市间的二维欧几里得距离 29 | def getDist(): 30 | global map,size 31 | dist = np.zeros((size,size)) 32 | for i in range(0,size): 33 | for j in range(0,size): 34 | dist[i][j] = ((map[i][0]-map[j][0])**2 + (map[i][1]-map[j][1])**2)**0.5 35 | return dist 36 | 37 | txt = "C:\\Users\\Cecilia\\Desktop\\TSP\\ch130.txt" 38 | map = load(txt) 39 | size = len(map) 40 | visited = {} 41 | solutions = [] 42 | DIST = getDist() 43 | count = 0 44 | M = 30 #种群大小 45 | 46 | 47 | #根据路径获取该路径总代价 48 | def getCost(path): 49 | cost = 0 50 | former = path[0] 51 | for city in path: 52 | cost += DIST[former][city] 53 | former = city 54 | cost += DIST[path[0]][path[-1]] 55 | return cost 56 | 57 | #Partial-Mapped crossover 58 | def PMX(i,j): 59 | global size 60 | s,t = sorted(random.sample(range(1,size),2)) 61 | next_i = list(i[:s] + j[s:t] + i[t:]) 62 | next_j = list(j[:s] + i[s:t] + j[t:]) 63 | #建立映射表 64 | mapped_i = {next_i[k]:next_j[k] for k in range(s,t)} 65 | mapped_j = {next_j[k]:next_i[k] for k in range(s,t)} 66 | #判断是否满足解的条件(每个城市皆访问一次) 67 | while len(set(next_i)) != len(next_i): 68 | for k in range(size): 69 | if k < t and k >= s: 70 | continue 71 | while next_i[k] in j[s:t]: 72 | next_i[k] = mapped_i[next_i[k]] 73 | while len(set(next_j)) != len(next_j): 74 | for k in range(size): 75 | if k < t and k >= s: 76 | continue 77 | while next_j[k] in i[s:t]: 78 | next_j[k] = mapped_j[next_j[k]] 79 | next_i = tuple(next_i) 80 | next_j = tuple(next_j) 81 | if next_i not in visited: 82 | visited.update({next_i:getCost(next_i)}) 83 | if next_j not in visited: 84 | visited.update({next_j:getCost(next_j)}) 85 | return next_i,next_j 86 | 87 | 88 | 89 | 90 | #反转一段区间,获取新邻域 91 | def reverse(path): 92 | global size 93 | min = 1000000000 94 | for cnt in range(100): 95 | i,j = sorted(random.sample(range(1,size-1),2)) 96 | path_ = path[:i] + path[i:j+1][::-1] + path[j+1:] 97 | if path_ not in visited: 98 | cost = getCost(path_) 99 | visited.update({path_:cost}) 100 | else: 101 | cost = visited[path_] 102 | if cost < visited[path]: 103 | min = cost 104 | p = path_ 105 | break 106 | if cost < min: 107 | min = cost 108 | p = path_ 109 | return p 110 | 111 | 112 | #交换两个城市,获取新邻域 113 | def exchange(path): 114 | global size 115 | min = 1000000000 116 | for cnt in range(100): 117 | i,j = sorted(random.sample(range(1,size-1),2)) 118 | path_ = path[:i] + path[j:j+1] + path[i+1:j] + path[i:i+1] + path[j+1:] 119 | if path_ not in visited: 120 | cost = getCost(path_) 121 | visited.update({path_:cost}) 122 | else: 123 | cost = visited[path_] 124 | if cost < visited[path]: 125 | min = cost 126 | p = path_ 127 | break 128 | if cost < min: 129 | min = cost 130 | p = path_ 131 | return p 132 | 133 | #随机挑选两个城市插入序列头部,获取新邻域 134 | def insert(path): 135 | global size,solutions 136 | min = 1000000000 137 | for cnt in range(100): 138 | i,j = sorted(random.sample(range(1,size-1),2)) 139 | path_ = path[i:i+1] + path[j:j+1] + path[:i] + path[i+1:j] + path[j+1:] 140 | if path_ not in visited: 141 | cost = getCost(path_) 142 | visited.update({path_:cost}) 143 | else: 144 | cost = visited[path_] 145 | if cost < visited[path]: 146 | min = cost 147 | p = path_ 148 | break 149 | if cost < min: 150 | min = cost 151 | p = path_ 152 | return p 153 | 154 | 155 | #遗传算法 156 | def GA(paths,kmax): 157 | global M,solutions 158 | temp = paths 159 | for k in range(kmax): 160 | count = 0 161 | flag = 0 162 | children = [] #存储此代交叉、变异产生的子种群 163 | #加入当前种群中的最优解,使得下一代种群的最优解一定不会劣于当前种群最优解 164 | children.append(temp[0]) 165 | for l in range(M): 166 | while True: 167 | cur = sorted(temp[:], key=lambda x:visited[x])[0] 168 | i = random.randrange(M) 169 | count+=1 170 | if temp[i] != cur: 171 | break 172 | if count > 100000: 173 | flag = 1 174 | break 175 | if flag == 0: 176 | a,b = PMX(temp[i],cur) #使用PMX交叉操作 177 | children.append(a) 178 | children.append(b) 179 | for l in range(M): 180 | i = random.randrange(M) 181 | children.append(reverse(temp[i])) #使用反转作为变异操作 182 | temp = sorted(children[:], key=lambda x:visited[x])[:M] #选取子代中最优的前M个解 183 | solutions.append(visited[temp[0]]) #记录此次迭代产生的下一代的最优解 184 | #print(k,visited[temp[0]]) 185 | return temp[0] 186 | 187 | 188 | 189 | def main(): 190 | global visited,size,map,M,solutions 191 | kmax = 1300 192 | for i in range(8): 193 | time_start = time.time() 194 | start = [tuple(random.sample(range(size),size)) for m in range(M)] 195 | visited = {key:getCost(key) for key in start} 196 | path = GA(start,kmax) 197 | cost = visited[path] 198 | path = path[:] + path[:1] 199 | time_end = time.time() 200 | print() 201 | #print('Algorithm GA iterated',kmax,'times!\n',sep=' ') 202 | best = 6110 203 | print(time_end-time_start,cost,(cost-best)/best,sep=" ") #此处单位为秒 204 | '''print('You got the best solution:',cost,sep='\n') 205 | print(path) 206 | 207 | print("误差为:",(cost-best)/best)''' 208 | 209 | '''x = np.array([map[i][0] for i in path]) 210 | y = np.array([map[i][1] for i in path]) 211 | i = np.arange(0,len(solutions)) 212 | solutions = np.array(solutions) 213 | plt.subplot(121) 214 | plt.plot(x,y) 215 | plt.subplot(122) 216 | plt.plot(i,solutions) 217 | plt.show()''' 218 | 219 | 220 | main() 221 | 222 | 223 | --------------------------------------------------------------------------------