Author Topic: Traveling Salesperson  (Read 5906 times)

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Traveling Salesperson
« on: March 31, 2019, 11:09:00 am »
I was curious last night how fast I could throw together some tools to experiment with the Traveling Salesperson problem, about an hour to get enough going to do something (Shiffman has done a number of interesting topics, I started on this one last night):
Code: QB64: [Select]
  1. _TITLE "Traveling Salesperson" 'B+ started  2019-03-31
  2. 'put together some subroutines
  3.  
  4. CONST xmax = 800
  5. CONST ymax = 600
  6. CONST nPoints = 10
  7.  
  8. SCREEN _NEWIMAGE(xmax, ymax, 32)
  9. _SCREENMOVE 300, 40
  10. TYPE pointType
  11.     x AS SINGLE
  12.     y AS SINGLE
  13. DIM SHARED Points(1 TO nPoints) AS pointType
  14. DIM SHARED CopyPts(1 TO nPoints) AS pointType
  15.  
  16. smallD = 1000000
  17. makePoints
  18. WHILE _KEYDOWN(27) = 0
  19.     CLS
  20.     showPoints
  21.     showRoute
  22.     d = dTrip!
  23.     PRINT d
  24.     IF d < smallD THEN copyPoints: smallD = d
  25.     shufflePoints
  26.     count = count + 1
  27.     PRINT "Press any for next or escape for smallest route found.... "
  28.     SLEEP
  29. PRINT "From "; count; " random routes, this was the shortest:"
  30. reloadPoints
  31. PRINT "with distance of"; smallD
  32.  
  33. SUB makePoints
  34.     FOR i = 1 TO nPoints
  35.         Points(i).x = RND * xmax
  36.         Points(i).y = RND * ymax
  37.     NEXT
  38.  
  39. SUB shufflePoints
  40.     FOR i = nPoints TO 2 STEP -1
  41.         r = INT(RND * i) + 1
  42.         SWAP Points(i), Points(r)
  43.     NEXT
  44.  
  45. SUB copyPoints
  46.  
  47.     FOR i = 1 TO nPoints
  48.         CopyPts(i).x = Points(i).x
  49.         CopyPts(i).y = Points(i).y
  50.     NEXT
  51.  
  52. SUB reloadPoints
  53.     FOR i = 1 TO nPoints
  54.         Points(i).x = CopyPts(i).x
  55.         Points(i).y = CopyPts(i).y
  56.     NEXT
  57.  
  58. FUNCTION dTrip! ()
  59.     FOR i = 1 TO nPoints
  60.         IF i = nPoints THEN
  61.             d = d + SQR((Points(i).x - Points(1).x) ^ 2 + (Points(i).y - Points(1).y) ^ 2)
  62.         ELSE
  63.             d = d + SQR((Points(i).x - Points(i + 1).x) ^ 2 + (Points(i).y - Points(i + 1).y) ^ 2)
  64.         END IF
  65.     NEXT
  66.     dTrip! = d
  67.  
  68. SUB showPoints
  69.     FOR i = 1 TO nPoints
  70.         CIRCLE (Points(i).x, Points(i).y), 2
  71.     NEXT
  72.  
  73. SUB showRoute
  74.     FOR i = 1 TO nPoints
  75.         IF i = nPoints THEN
  76.             LINE (Points(i).x, Points(i).y)-(Points(1).x, Points(1).y)
  77.         ELSE
  78.             LINE (Points(i).x, Points(i).y)-(Points(i + 1).x, Points(i + 1).y)
  79.         END IF
  80.     NEXT
  81.  
  82.  
  83.  

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #1 on: March 31, 2019, 11:20:23 am »
OK before someone asks for background on this, we will let Shiffman set it up:


I watched about half of this and then just challenged myself to code what he had so far accomplished.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #2 on: March 31, 2019, 12:37:37 pm »
Hmm... Shiffman is not returning to the start city (yet?).

Wiki ref:
Quote
The travelling salesman problem (TSP) asks the following question: "Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city and returns to the origin city?" It is an NP-hard problem in combinatorial optimization, important in operations research and theoretical computer science.

Also we are given Points or City coordinates not a distances matrix. (so far)

I mention this because my tools were assuming round trips.
« Last Edit: March 31, 2019, 12:46:30 pm by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #3 on: March 31, 2019, 01:33:59 pm »
Oh I like what he did at the end of first Part, he is displaying the best path found underneath the current test path:

Here is that adding thic line sub with fTri for that sub, best path is in purple thic lines.

Code: QB64: [Select]
  1. _TITLE "Traveling Salesperson" 'B+ started  2019-03-31
  2. '2019-03-31 #2 add thic line sub and ftri for that to show best path found so far
  3.  
  4. CONST xmax = 800
  5. CONST ymax = 600
  6. CONST nPoints = 10
  7.  
  8. SCREEN _NEWIMAGE(xmax, ymax, 32)
  9. _SCREENMOVE 300, 40
  10. TYPE pointType
  11.     x AS SINGLE
  12.     y AS SINGLE
  13. DIM SHARED Points(1 TO nPoints) AS pointType
  14. DIM SHARED CopyPts(1 TO nPoints) AS pointType
  15.  
  16. smallD = 1000000
  17. makePoints
  18. WHILE _KEYDOWN(27) = 0
  19.     CLS
  20.     IF count > 0 THEN showCopy
  21.     showPoints
  22.     showRoute
  23.     d = dTrip!
  24.     PRINT "Round trip distance:"; d
  25.     IF d < smallD THEN copyPoints: smallD = d
  26.     shufflePoints
  27.     count = count + 1
  28.     PRINT "Press any for next or escape for smallest route found.... "
  29.     SLEEP
  30. PRINT "From "; count; " random routes, this was the shortest:"
  31. reloadPoints
  32. showCopy
  33. PRINT "with distance of"; smallD
  34.  
  35. SUB makePoints
  36.     FOR i = 1 TO nPoints
  37.         Points(i).x = RND * xmax
  38.         Points(i).y = RND * ymax
  39.     NEXT
  40.  
  41. SUB shufflePoints
  42.     FOR i = nPoints TO 2 STEP -1
  43.         r = INT(RND * i) + 1
  44.         SWAP Points(i), Points(r)
  45.     NEXT
  46.  
  47. SUB copyPoints
  48.     FOR i = 1 TO nPoints
  49.         CopyPts(i).x = Points(i).x
  50.         CopyPts(i).y = Points(i).y
  51.     NEXT
  52.  
  53. SUB reloadPoints
  54.     FOR i = 1 TO nPoints
  55.         Points(i).x = CopyPts(i).x
  56.         Points(i).y = CopyPts(i).y
  57.     NEXT
  58.  
  59. FUNCTION dTrip! ()
  60.     FOR i = 1 TO nPoints
  61.         IF i = nPoints THEN
  62.             d = d + SQR((Points(i).x - Points(1).x) ^ 2 + (Points(i).y - Points(1).y) ^ 2)
  63.         ELSE
  64.             d = d + SQR((Points(i).x - Points(i + 1).x) ^ 2 + (Points(i).y - Points(i + 1).y) ^ 2)
  65.         END IF
  66.     NEXT
  67.     dTrip! = d
  68.  
  69. SUB showPoints
  70.     FOR i = 1 TO nPoints
  71.         CIRCLE (Points(i).x, Points(i).y), 2
  72.     NEXT
  73.  
  74. SUB showRoute
  75.     FOR i = 1 TO nPoints
  76.         IF i = nPoints THEN
  77.             LINE (Points(i).x, Points(i).y)-(Points(1).x, Points(1).y)
  78.         ELSE
  79.             LINE (Points(i).x, Points(i).y)-(Points(i + 1).x, Points(i + 1).y)
  80.         END IF
  81.     NEXT
  82.  
  83. SUB showCopy
  84.     FOR i = 1 TO nPoints
  85.         IF i = nPoints THEN
  86.             thic CopyPts(i).x, CopyPts(i).y, CopyPts(1).x, CopyPts(1).y, 5, _RGB32(200, 0, 100)
  87.         ELSE
  88.             thic CopyPts(i).x, CopyPts(i).y, CopyPts(i + 1).x, CopyPts(i + 1).y, 5, _RGB32(200, 0, 100)
  89.         END IF
  90.     NEXT
  91.  
  92. SUB thic (x1, y1, x2, y2, thick, K AS _UNSIGNED LONG)
  93.     pd2 = _PI(.5)
  94.     t2 = thick / 2
  95.     IF t2 < 1 THEN t2 = 1
  96.     a = _ATAN2(y2 - y1, x2 - x1)
  97.     x3 = x1 + t2 * COS(a + pd2)
  98.     y3 = y1 + t2 * SIN(a + pd2)
  99.     x4 = x1 + t2 * COS(a - pd2)
  100.     y4 = y1 + t2 * SIN(a - pd2)
  101.     x5 = x2 + t2 * COS(a + pd2)
  102.     y5 = y2 + t2 * SIN(a + pd2)
  103.     x6 = x2 + t2 * COS(a - pd2)
  104.     y6 = y2 + t2 * SIN(a - pd2)
  105.     ftri x6, y6, x4, y4, x3, y3, K
  106.     ftri x3, y3, x5, y5, x6, y6, K
  107.  
  108. ' found at [abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]:    http://www.[abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]/forum/index.php?topic=14425.0
  109. SUB ftri (x1, y1, x2, y2, x3, y3, K AS _UNSIGNED LONG)
  110.     a& = _NEWIMAGE(1, 1, 32)
  111.     _DEST a&
  112.     PSET (0, 0), K
  113.     _DEST 0
  114.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, 0)-(0, 0), a& TO(x1, y1)-(x2, y2)-(x3, y3)
  115.     _FREEIMAGE a& '<<< this is important!
  116.  
  117.  

 
TSP Best Path.PNG


I have a theory already: after leaning on spacebar and watching the Best Path untangle, the best path is not going to have any or fewest intersecting lines; it is certainly the nicest to look at if you like simplicity.

So how do we find that in code?   onto part 2

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #4 on: March 31, 2019, 03:45:44 pm »
The next part of Shiffman's Traveling Salesperson series side tracks to the subject of generating Permutations.

Frankly following Shiffman through "Lexicographic ordering" with JS subroutines is difficult hurdle. Fortunately I already have very nice Permutation generating code which I will post in separate thread because it comes in handy now and then. It is probably the very same algorithm Shiffman uses.
« Last Edit: March 31, 2019, 03:47:03 pm by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #5 on: March 31, 2019, 10:18:14 pm »

OK I did go step by step again with Shiffman in the Lexicographic Order lecture and did get a successful Permutations generator.

I did this because I don't think we want to work with an array of strings that we would have to translate back into indexes. It would be better to generate each new array update and then test the Trip Distances.

Anyway this code seems to work:
Code: QB64: [Select]
  1. _TITLE "Lexicographical Order" 'B+ 2019-03-31
  2. 'start with array whose values ascend
  3. 'goal end with an array whose values all descend
  4.  
  5. SCREEN _NEWIMAGE(600, 700, 32)
  6. _SCREENMOVE 300, 20
  7. DIM SHARED P(4) AS INTEGER 'load P
  8. FOR i = 0 TO UBOUND(P)
  9.     P(i) = i
  10. 'showP
  11. 'END
  12. showP
  13.     'step 1 Find largest index x st P(x) < P(x +1)
  14.     bigX = -1 'invalid index at end says we are done
  15.     FOR x = 0 TO UBOUND(P) - 1
  16.         IF P(x) < P(x + 1) THEN bigX = x
  17.     NEXT
  18.     IF bigX = -1 THEN EXIT DO
  19.     'PRINT bigX
  20.     'END
  21.  
  22.     'step 2 Find largest y  st P(x) < P(y)
  23.     bigY = -1
  24.     FOR y = 0 TO UBOUND(P)
  25.         IF P(bigX) < P(y) THEN bigY = y
  26.     NEXT
  27.     'PRINT bigY
  28.     'END
  29.  
  30.     'step 3 swap P(bigI) , P(bigJ)
  31.     SWAP P(bigX), P(bigY)
  32.     'showP
  33.     'END
  34.    
  35.     'QB64 has no slice! or any of that JS stuff, have to do this by hand
  36.     'step 4 Reverse P(x+1 ... n)   = n - (x+1) -1 elements ??? let the computer count it out!
  37.     count = 0
  38.     FOR r = bigX + 1 TO UBOUND(P)
  39.         count = count + 1
  40.     NEXT
  41.     'PRINT count
  42.     'END
  43.  
  44.     REDIM temp(count)
  45.     count = 0
  46.     FOR r = UBOUND(P) TO bigX + 1 STEP -1
  47.         temp(count) = P(r)
  48.         'PRINT temp(count)
  49.         count = count + 1
  50.     NEXT
  51.     ' ? 4
  52.  
  53.     count = 0
  54.     FOR r = bigX + 1 TO UBOUND(P)
  55.         P(r) = temp(count)
  56.         count = count + 1
  57.     NEXT
  58.     showP
  59.     INPUT "OK... press Enter", wate$
  60.  
  61. PRINT "Done"
  62.  
  63.  
  64. SUB showP
  65.     FOR i = 0 TO UBOUND(P)
  66.         PRINT P(i);
  67.     NEXT
  68.     PRINT
  69.  
« Last Edit: March 31, 2019, 10:32:40 pm by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #6 on: April 01, 2019, 04:56:52 pm »
After the adventure of Lexicographical Order, it is almost anti-climatic installing it into Traveling Salesperson code and using it. Kind of tricky how you use the Order to reset the Points but nothing like duplicating the slice, reverse and concat functions of JS.

So here is Traveling Salesperson code checking through every permutation of paths though cities and finding the shortest route, round trip. For n cities there will be 2 * n shortest trips at least. Because it is a closed loop, you can start at any city and you could go in either direction to one of the two cities the route connects.

Code: QB64: [Select]
  1. _TITLE "Traveling Salesperson Find Shortest Round Trip" 'B+ started  2019-04-01
  2. '2019-03-31 #2 add thic line sub and ftri for that to show best path found so far
  3. '2019-04-01 starting with Traveling Salesperson, make the Points array 0 based and add ID to PointType.
  4. 'fix code to handle the base 0 change
  5. 'add lexicographic order code to replace shuffle, start array Order to hold the current Order being tested.
  6.  
  7. ' OK Traveling Salesman Find Shortest Round Trip is set to run through all Permutations of Points
  8. ' to find the shortest path among them. (There will be nPoints * 2 minimum shortest paths
  9. ' because round trips are closed loops, one could start at any city and go clockwise or CCW to visit every
  10. ' city.
  11.  
  12. CONST xmax = 1200
  13. CONST ymax = 600
  14. CONST nPoints = 12 '3 trivial they all are shortest!
  15.  
  16. SCREEN _NEWIMAGE(xmax, ymax, 32)
  17. _SCREENMOVE 100, 40
  18.  
  19. TYPE pointType
  20.     ID AS INTEGER
  21.     x AS SINGLE
  22.     y AS SINGLE
  23.  
  24. DIM SHARED Points(0 TO nPoints - 1) AS pointType
  25. DIM SHARED CopyPts(0 TO nPoints - 1) AS pointType
  26. DIM SHARED Order(0 TO nPoints - 1) AS INTEGER
  27.  
  28. 'load Order
  29. FOR i = 0 TO nPoints - 1
  30.     Order(i) = i
  31.  
  32. ' initialize
  33. DIM SHARED LexiDone, smallD 'signal from Lexi that it has run all perms
  34. nTests&& = factorial&&(nPoints)
  35. smallD = 1000000
  36. makePoints 'ID's will match up with Order()
  37.  
  38. 'main loop
  39. WHILE _KEYDOWN(27) = 0 AND LexiDone = 0
  40.     CLS
  41.     IF count > 0 THEN showCopy
  42.     showPoints
  43.     showRoute
  44.     d = dTrip!
  45.     count = count + 1
  46.     PRINT "Round trip distance:"; d; "for"; count; "test of"; nTests&&; "total tests to be made."
  47.     IF d < smallD THEN copyPoints: smallD = d
  48.     'shufflePoints
  49.  
  50.     'Here we update the Lexicographical Order in Order array
  51.     updateOrder ' and then will swap the order of points for next test.
  52.  
  53.     _DISPLAY
  54.  
  55.     'toggle comments, to go through a few arrangements slowly
  56.     _LIMIT 500
  57.     'PRINT "Press any for next or escape for smallest route found.... "
  58.     'SLEEP
  59.  
  60.  
  61. 'display final results
  62. PRINT "From"; count; "random routes, this was the shortest:"
  63. reloadPoints
  64. showPoints
  65. showRoute
  66. 'showCopy
  67. PRINT "with distance of"; smallD
  68.  
  69. FUNCTION factorial&& (n AS INTEGER)
  70.     f&& = 1
  71.     FOR i = 2 TO n
  72.         f&& = f&& * i
  73.     NEXT
  74.     factorial&& = f&&
  75.  
  76. SUB updateOrder 'from Lexicographical Order change P() to Order()
  77.     'step 1 Find largest index x st P(x) < P(x +1)
  78.  
  79.     bigX = -1 'invalid index at end says we are done
  80.     FOR x = 0 TO UBOUND(Order) - 1
  81.         IF Order(x) < Order(x + 1) THEN bigX = x
  82.     NEXT
  83.     IF bigX = -1 THEN LexiDone = 1: EXIT SUB
  84.  
  85.     'step 2 Find largest y  st P(x) < P(y)
  86.     bigY = -1
  87.     FOR y = 0 TO UBOUND(Order)
  88.         IF Order(bigX) < Order(y) THEN bigY = y
  89.     NEXT
  90.  
  91.     'step 3 swap P(bigI) , P(bigJ)
  92.     SWAP Order(bigX), Order(bigY)
  93.  
  94.     'QB64 has no slice! or any of that JS stuff, have to do this by hand
  95.     'step 4 Reverse P(x+1 ... n)   = n - (x+1) -1 elements ??? let the computer count it out!
  96.     count = 0
  97.     FOR r = bigX + 1 TO UBOUND(Order)
  98.         count = count + 1
  99.     NEXT
  100.  
  101.     REDIM temp(count)
  102.     count = 0
  103.     FOR r = UBOUND(Order) TO bigX + 1 STEP -1
  104.         temp(count) = Order(r)
  105.         count = count + 1
  106.     NEXT
  107.     count = 0
  108.     FOR r = bigX + 1 TO UBOUND(Order)
  109.         Order(r) = temp(count)
  110.         count = count + 1
  111.     NEXT
  112.  
  113.     'OK now rearrange points to Order()
  114.     FOR i = 0 TO nPoints - 2 'only have to go to secound to last
  115.         targetID = Order(i)
  116.         IF Points(i).ID <> targetID THEN 'find where it is and swap
  117.             FOR j = i + 1 TO nPoints - 1
  118.                 IF Points(j).ID = targetID THEN
  119.                     SWAP Points(i), Points(j)
  120.                     EXIT FOR
  121.                 END IF
  122.             NEXT
  123.         END IF
  124.     NEXT
  125.  
  126.  
  127. SUB makePoints
  128.     FOR i = 0 TO nPoints - 1
  129.         Points(i).ID = i
  130.         Points(i).x = RND * (xmax - 20) * .5 + 5
  131.         Points(i).y = RND * (ymax - 40) + 40
  132.     NEXT
  133.  
  134. SUB shufflePoints
  135.     FOR i = nPoints - 1 TO 1 STEP -1
  136.         r = INT(RND * (i + 1))
  137.         SWAP Points(i), Points(r)
  138.     NEXT
  139.  
  140. SUB copyPoints
  141.     FOR i = 0 TO nPoints - 1
  142.         CopyPts(i).x = Points(i).x
  143.         CopyPts(i).y = Points(i).y
  144.     NEXT
  145.  
  146. SUB reloadPoints
  147.     FOR i = 0 TO nPoints - 1
  148.         Points(i).x = CopyPts(i).x
  149.         Points(i).y = CopyPts(i).y
  150.     NEXT
  151.  
  152. FUNCTION dTrip! ()
  153.     FOR i = 0 TO nPoints - 1
  154.         IF i = nPoints - 1 THEN
  155.             d = d + SQR((Points(i).x - Points(0).x) ^ 2 + (Points(i).y - Points(0).y) ^ 2)
  156.         ELSE
  157.             d = d + SQR((Points(i).x - Points(i + 1).x) ^ 2 + (Points(i).y - Points(i + 1).y) ^ 2)
  158.         END IF
  159.     NEXT
  160.     dTrip! = d
  161.  
  162. SUB showPoints
  163.     FOR i = 0 TO nPoints - 1
  164.         CIRCLE (Points(i).x, Points(i).y), 2
  165.     NEXT
  166.  
  167. SUB showRoute
  168.     FOR i = 0 TO nPoints - 1
  169.         IF i = nPoints - 1 THEN
  170.             LINE (Points(i).x, Points(i).y)-(Points(0).x, Points(0).y)
  171.         ELSE
  172.             LINE (Points(i).x, Points(i).y)-(Points(i + 1).x, Points(i + 1).y)
  173.         END IF
  174.     NEXT
  175.  
  176. SUB showCopy
  177.     _PRINTSTRING (xmax / 2 + 200 + 20, 5), "Shortest Trip (so far):" + STR$(smallD)
  178.     FOR i = 0 TO nPoints - 1
  179.         IF i = nPoints - 1 THEN
  180.             thic CopyPts(i).x + .5 * xmax, CopyPts(i).y, CopyPts(0).x + .5 * xmax, CopyPts(0).y, 2, _RGB32(200, 0, 100)
  181.         ELSE
  182.             thic CopyPts(i).x + .5 * xmax, CopyPts(i).y, CopyPts(i + 1).x + .5 * xmax, CopyPts(i + 1).y, 2, _RGB32(200, 0, 100)
  183.         END IF
  184.     NEXT
  185.     FOR i = 0 TO nPoints - 1
  186.         CIRCLE (CopyPts(i).x + .5 * xmax, CopyPts(i).y), 3
  187.     NEXT
  188.  
  189. SUB thic (x1, y1, x2, y2, thick, K AS _UNSIGNED LONG)
  190.     pd2 = _PI(.5)
  191.     t2 = thick / 2
  192.     IF t2 < 1 THEN t2 = 1
  193.     a = _ATAN2(y2 - y1, x2 - x1)
  194.     x3 = x1 + t2 * COS(a + pd2)
  195.     y3 = y1 + t2 * SIN(a + pd2)
  196.     x4 = x1 + t2 * COS(a - pd2)
  197.     y4 = y1 + t2 * SIN(a - pd2)
  198.     x5 = x2 + t2 * COS(a + pd2)
  199.     y5 = y2 + t2 * SIN(a + pd2)
  200.     x6 = x2 + t2 * COS(a - pd2)
  201.     y6 = y2 + t2 * SIN(a - pd2)
  202.     ftri x6, y6, x4, y4, x3, y3, K
  203.     ftri x3, y3, x5, y5, x6, y6, K
  204.  
  205. ' found at [abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]:    http://www.[abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]/forum/index.php?topic=14425.0
  206. SUB ftri (x1, y1, x2, y2, x3, y3, K AS _UNSIGNED LONG)
  207.     a& = _NEWIMAGE(1, 1, 32)
  208.     _DEST a&
  209.     PSET (0, 0), K
  210.     _DEST 0
  211.     _MAPTRIANGLE _SEAMLESS(0, 0)-(0, 0)-(0, 0), a& TO(x1, y1)-(x2, y2)-(x3, y3)
  212.     _FREEIMAGE a& '<<< this is important!
  213.  
  214.  

By the time this code finishes, you would have the best route pretty much figured out.

And I haven't seen a best route yet that had lines that crossed!
Traveling Salesperson Finding best Route on 12 cities.PNG
* Traveling Salesperson Finding best Route on 12 cities.PNG (Filesize: 22.46 KB, Dimensions: 1203x625, Views: 229)
prediction.PNG
* prediction.PNG (Filesize: 33.52 KB, Dimensions: 1199x631, Views: 206)
« Last Edit: April 01, 2019, 05:04:37 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Traveling Salesperson
« Reply #7 on: April 01, 2019, 05:57:23 pm »
A thought I’ve had on this:  Imagine an X/Y axis through the center of the screen.   Find the farthest point from that center, and rotate a line, like on a clock, either left or right, and connect the points in the order you touch them...   

That’s your route.

I don’t think permutations of travel are needed in most cases.  Just simple rotational lines from one outer point to the next.  If my time frees up a bit again soon, I’ll try and work up an example to see if it’d truly work for results like I’d think it should.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #8 on: April 01, 2019, 06:14:03 pm »
Thanks Steve, this might give you a leg up on the task: https://www.qb64.org/forum/index.php?topic=1185.0

I've been itching to try something with it myself. Trouble is with a few points we don't see how the enclosed points get connected.

The next thing Shiffman does is Mutations of the better paths.

I've been thinking about saving time like as soon as distance exceeds the smallest found so far to drop the test and move to next permutation.

Plus there is something about NOT crossing lines, it is at least a rule of thumb.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #9 on: April 02, 2019, 10:45:50 am »
Oh man! I think I have a really elegant method for this problem.

I wasn't inspired by Shiffman's Mutation method which would still need verification as the Best trip possible of all permutations. And it could happen, you start trying to mutate an already best trip!

On the lines of what Steve mentioned, a sort of Divide and Conquer approach, my thinking started with 12 pts have 12! = 479,000,001,600 permutations but optimizing 2 sets of 6 points is 6! + 6! = 2 * 720, just figure a way to connect (mutate) from those two solution subsets? Again, I mention that after doing all permutations of one point at one position, you will have bagged 2 best trip distances because it is a closed loop that includes every point, so you can start at any point and go in 2 directions on the Best Loop.

While wondering about connecting the two optimized sets, the idea came to me when I flipped the problem around.



« Last Edit: April 02, 2019, 10:49:50 am by bplus »

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Traveling Salesperson
« Reply #10 on: April 02, 2019, 11:49:56 am »
If you expound on this idea "optimizing 2 sets of 6 points" why can't you start with 6 sets of 2 points. All the 2 points sets are preoptimized. :)
QB64 is the best!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #11 on: April 02, 2019, 12:12:50 pm »
If you expound on this idea "optimizing 2 sets of 6 points" why can't you start with 6 sets of 2 points. All the 2 points sets are preoptimized. :)

Any 3 (non collinear points) are an optimized AND complete loop! Good place to start a building, I thought.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #12 on: April 02, 2019, 03:39:15 pm »
Update:
Well the simple idea of finding which connected pair of points a new point is closest to, to add to a closed loop by connecting the new Point to one end of each pair did not work. I thought I might build the shortest route up from 3 points with that method. Perhaps a more complex variation will still work.

The curse of the intersecting lines between cities strikes again.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #13 on: April 03, 2019, 11:04:20 pm »
Here is current state of Traveling Salespeople code, if anyone curious or wants to offer suggestions:
It is building pretty good routes from a points list and maybe 50/50 chance it is best route.

Code: QB64: [Select]
  1. _TITLE "Traveling Salesperson Find Shortest Round Trip 2" 'B+  2019-04-01
  2. '2019-03-31 #2 add thic line sub and ftri for that to show best path found so far
  3. '2019-04-01 starting with Traveling Salesperson, make the Points array 0 based and add ID to PointType.
  4. 'fix code to handle the base 0 change
  5. 'add lexicographic order code to replace shuffle, start array Order to hold the current Order being tested.
  6.  
  7. ' OK Traveling Salesman Find Shortest Rount Trip is set to run through all Permutations of Points
  8. ' to find the shortest path amoung them. (There will be nPoints * 2 minimun shortest paths
  9. ' because roundtrips are closed loops, one could start at any city and go clockwise or CCW to visit every
  10. ' city.
  11.  
  12. ' Traveling Salesperson Find Shortest Round Trip 2, save time as soon as the distance exceeds smallD move on.
  13. ' Not going to work...
  14.  
  15. ' 2019-04-02 Overhaul this to test a new approach to the finding the shortest trip.
  16. ' make a list of points
  17. ' make an array called connected and add the first 3 points of list to it, already shortest path
  18. ' start loop from 4 th point to nPoints
  19. ' get next point on list and compare distances from new point to all pairs of connected points
  20. ' insert (connect) the new point between the pair of conncted points
  21. ' loop to next get point until all points on list exhausted.
  22.  
  23. ' Save old permutations check to verify we are building smallest trips
  24. '2019-04-02 OK works but results are disappointing, need to see what is going on when adding points to build
  25.  
  26. '2019-04-02 OK build up the route from a points list by checking new point between every other point for
  27. ' shortest trip, I have not seen many crossed or intersecting lines, but running throuh all permutations
  28. ' there is often a better route, 50/50?
  29.  
  30. '2019-04-03 added Optimizer sub that takes the Connected() and runs through all the points again between
  31. 'all the other points looking for a smaller path, sometimes it finds, one 1/10?
  32.  
  33. 'BTW after half permutations run, I end checking because you wont get any better path after that.
  34.  
  35. CONST xmax = 1200
  36. CONST ymax = 600
  37. CONST nPoints = 10 '3 trivial they all are shortest!   more than 10 is agonizingly long to run all permutations
  38.  
  39. SCREEN _NEWIMAGE(xmax, ymax, 32)
  40. _SCREENMOVE 100, 40
  41.  
  42. TYPE pointType
  43.     ID AS INTEGER
  44.     x AS SINGLE
  45.     y AS SINGLE
  46.  
  47. ''start of building a shortest Trip by adding one point at a time to already connected shortest path  ----------------------------
  48. DIM SHARED PointsList(0 TO nPoints - 1) AS pointType
  49. DIM SHARED Points(0 TO nPoints - 1) AS pointType
  50. DIM SHARED CopyPts(nPoints - 1) AS pointType
  51. DIM SHARED Order(0 TO nPoints - 1) AS INTEGER
  52. DIM SHARED LexiDone, smallD 'signal from Lexi that it has run all perms
  53. restart:
  54. makePointsList 'ID's will match up with Order() later
  55. REDIM SHARED Connected(0 TO 2) AS pointType
  56. FOR i = 0 TO 2 'add first three points on list to connected since any 3 points are already shortest trip
  57.     Connected(i) = PointsList(i)
  58. showArr Connected(), 0
  59. PRINT "Here is our starting Triangle."
  60. 'FOR i = 0 TO 2
  61. '    PRINT Connected(i).ID, Connected(i).x, Connected(i).y
  62. 'NEXT
  63. 'end
  64. 'start adding one point at a time to connected, hoping this method preserves shortest trip
  65. FOR ListPointer = 3 TO nPoints - 1
  66.     'in pairs of connected points find the smallest distand to the two points
  67.     smallestDD = 10 ^ 6 'invalid
  68.     lbc = LBOUND(connected): ubc = UBOUND(connected)
  69.  
  70.     'PRINT: PRINT "Here is Connected Array before new Point insertion:"
  71.     'FOR i = LBOUND(connected) TO UBOUND(Connected)
  72.     '    PRINT Connected(i).ID, Connected(i).x, Connected(i).y
  73.     'NEXT
  74.     'INPUT "OK press enter... :", wate$
  75.  
  76.     REDIM _PRESERVE Connected(lbc TO ubc + 1) AS pointType
  77.     Connected(ubc + 1) = PointsList(ListPointer)
  78.     insertPointer = ubc + 1
  79.     FOR insertPointer = ubc + 1 TO lbc STEP -1
  80.         d = dTrip!(Connected())
  81.         IF d < smallestDD THEN smallestDD = d: savePointer = insertPointer
  82.  
  83.         'PRINT: PRINT "InsetPointer at"; insertPointer; "distance here ="; d; "savePointer is"; savePointer
  84.         'FOR i = LBOUND(connected) TO UBOUND(connected)
  85.         '    PRINT Connected(i).ID, Connected(i).x, Connected(i).y
  86.         'NEXT
  87.         'INPUT "OK press enter... :", wate$
  88.  
  89.         'setup next
  90.         IF insertPointer <> lbc THEN
  91.             SWAP Connected(insertPointer), Connected(insertPointer - 1)
  92.         END IF
  93.     NEXT
  94.     FOR insertPointer = lbc TO ubc
  95.         SWAP Connected(insertPointer), Connected(insertPointer + 1)
  96.     NEXT
  97.  
  98.     ''ok the new point should be all the way around to the end again
  99.     'PRINT: PRINT "OK restored Connected at end insertion of new, save Pointer="; savePointer
  100.     'FOR i = LBOUND(connected) TO UBOUND(connected)
  101.     '    PRINT Connected(i).ID, Connected(i).x, Connected(i).y
  102.     'NEXT
  103.     'INPUT "OK press enter... :", wate$
  104.  
  105.     connectedPointer = ubc + 1
  106.     WHILE connectedPointer <> savePointer
  107.         SWAP Connected(connectedPointer), Connected(connectedPointer - 1)
  108.         connectedPointer = connectedPointer - 1
  109.     WEND
  110.  
  111.     'check insertion  and that should finish the loop
  112.     'PRINT: PRINT "Check insertion:"
  113.     'FOR i = LBOUND(connected) TO UBOUND(Connected)
  114.     '    PRINT Connected(i).ID, Connected(i).x, Connected(i).y
  115.     'NEXT
  116.     'INPUT "OK press enter... :", wate$
  117.  
  118.     CLS
  119.     showArr Connected(), 0
  120.  
  121.     _DELAY 1
  122.  
  123. showArr Connected(), 0
  124. PRINT " Here is the map built up from list of points, hopefully close to shortest path..."
  125. INPUT "OK press enter to try Optimizer...", wate$
  126.  
  127. Optimize Connected()
  128. PRINT "Here is that map run through the new Optimizer routine, did it help?"
  129. showArr Connected(), 0
  130. INPUT "OK press enter to run through all Permutations...", wate$
  131.  
  132. '-------------------------------------------------------------------------------------- end of new building shortest Path  code
  133. 'this is all for checking what we've built, code already works if I don't screw it up
  134.  
  135.  
  136. 'OK load this into Points and check if shortest path
  137. FOR i = 0 TO nPoints - 1
  138.     Points(i) = PointsList(i)
  139. copyPoints
  140.  
  141. 'load Order
  142. FOR i = 0 TO nPoints - 1
  143.     Order(i) = i
  144.  
  145. ' initialize checking
  146. nTests&& = factorial&&(nPoints)
  147. copyPoints
  148. smallD = dTrip!(Points()) ' get the distance of the points at the present load with the built path
  149. changedShortestPath = 0
  150. LexiDone = 0
  151. count = 0
  152. 'checking loop to confirm that the Connected() order is best
  153. WHILE _KEYDOWN(27) = 0 AND LexiDone = 0
  154.     'CLS
  155.     'showArr Points(), 0
  156.     'showArr CopyPts(), xmax / 2
  157.     '_PRINTSTRING (xmax / 2 + 125, 0), "Shortest Trip found systematically so far."
  158.     d = dTrip!(Points())
  159.     IF d < smallD THEN
  160.         CLS
  161.         showArr Points(), 0
  162.         showArr CopyPts(), xmax / 2
  163.         _PRINTSTRING (xmax / 2 + 125, 0), "Shortest Trip found systematically so far."
  164.         copyPoints
  165.         smallD = d
  166.         changedShortestPath = changedShortestPath + 1
  167.         _DISPLAY
  168.         _DELAY 2
  169.     END IF
  170.     count = count + 1
  171.     IF count MOD 100000 = 0 THEN
  172.         CLS
  173.         showArr Points(), 0
  174.         showArr CopyPts(), xmax / 2
  175.         _PRINTSTRING (xmax / 2 + 125, 0), "Shortest Trip found systematically so far."
  176.         LINE (0, 0)-STEP(xmax / 2 + 100, 20), _RGB32(0, 0, 0)
  177.         _PRINTSTRING (0, 0), "Round trip distance:" + STR$(d) + " for" + STR$(count) + " test of" + STR$(nTests&&) + " total tests to be made."
  178.         _DISPLAY
  179.     END IF
  180.  
  181.     '_DISPLAY
  182.     '_DELAY .01
  183.  
  184.     'Here we update the Lexicographical Order in Order array
  185.     updateOrder ' and then will swap the order of points for next test.
  186.     IF count > .5 * nTests&& THEN LexiDone = 1
  187.  
  188. 'display final results
  189. showArr Connected(), xmax / 2
  190. _PRINTSTRING (xmax / 2 + 200, 5), "Here is original path built:"
  191. PRINT "From"; count; "random routes, this was the shortest:"
  192. reloadPoints
  193. showArr Points(), 0
  194.  
  195. PRINT "with distance of"; smallD
  196. PRINT "Changed shortest path:"; changedShortestPath; "times since the build of the first set of Points."
  197. PRINT "press any to Restart... "
  198.  
  199. GOTO restart
  200.  
  201.  
  202. 'this sub runs through all the points and inserts each between all the other points looking for best distance
  203. 'but since we are far from ALL permutations this is not perfect system
  204. ' the builder code does a pretty good job connecting points for small path and this Optimizer does NOT change
  205. 'routes often.
  206. SUB Optimize (a() AS pointType)
  207.     lba = LBOUND(a): uba = UBOUND(a)
  208.     curD = dTrip!(a())
  209.     restart:
  210.     flag = 0
  211.     FOR pointer = lba TO uba
  212.         FOR p2 = lba TO uba
  213.             SWAP a(pointer), a(p2)
  214.             d = dTrip!(a())
  215.             IF d < curD THEN
  216.                 curD = d
  217.                 flag = 1
  218.             ELSE
  219.                 SWAP a(pointer), a(p2)
  220.             END IF
  221.         NEXT
  222.     NEXT
  223.     IF flag THEN GOTO restart
  224.  
  225.  
  226. FUNCTION factorial&& (n AS INTEGER)
  227.     f&& = 1
  228.     FOR i = 2 TO n
  229.         f&& = f&& * i
  230.     NEXT
  231.     factorial&& = f&&
  232.  
  233. SUB updateOrder 'from Lexicographical Order change P() to Order()
  234.     'step 1 Find largest index x st P(x) < P(x +1)
  235.  
  236.     bigX = -1 'invalid index at end says we are done
  237.     FOR x = 0 TO UBOUND(Order) - 1
  238.         IF Order(x) < Order(x + 1) THEN bigX = x
  239.     NEXT
  240.     IF bigX = -1 THEN LexiDone = 1: EXIT SUB
  241.  
  242.     'step 2 Find largest y  st P(x) < P(y)
  243.     bigY = -1
  244.     FOR y = 0 TO UBOUND(Order)
  245.         IF Order(bigX) < Order(y) THEN bigY = y
  246.     NEXT
  247.  
  248.     'step 3 swap P(bigI) , P(bigJ)
  249.     SWAP Order(bigX), Order(bigY)
  250.  
  251.     'QB64 has no slice! or any of that JS stuff, have to do this by hand
  252.     'step 4 Reverse P(x+1 ... n)   = n - (x+1) -1 elements ??? let the computer count it out!
  253.     count = 0
  254.     FOR r = bigX + 1 TO UBOUND(Order)
  255.         count = count + 1
  256.     NEXT
  257.  
  258.     REDIM temp(count)
  259.     count = 0
  260.     FOR r = UBOUND(Order) TO bigX + 1 STEP -1
  261.         temp(count) = Order(r)
  262.         count = count + 1
  263.     NEXT
  264.     count = 0
  265.     FOR r = bigX + 1 TO UBOUND(Order)
  266.         Order(r) = temp(count)
  267.         count = count + 1
  268.     NEXT
  269.  
  270.     'OK now rearrange points to Order()
  271.     FOR i = 0 TO nPoints - 2 'only have to go to secound to last
  272.         targetID = Order(i)
  273.         IF Points(i).ID <> targetID THEN 'find where it is and swap
  274.             FOR j = i + 1 TO nPoints - 1
  275.                 IF Points(j).ID = targetID THEN
  276.                     SWAP Points(i), Points(j)
  277.                     EXIT FOR
  278.                 END IF
  279.             NEXT
  280.         END IF
  281.     NEXT
  282.  
  283.  
  284. SUB makePointsList
  285.     FOR i = 0 TO nPoints - 1
  286.         PointsList(i).ID = i
  287.         PointsList(i).x = RND * (xmax - 20) * .5 + 5
  288.         PointsList(i).y = RND * (ymax - 40) + 40
  289.     NEXT
  290.  
  291. SUB copyPoints
  292.     FOR i = 0 TO nPoints - 1
  293.         CopyPts(i).x = Points(i).x
  294.         CopyPts(i).y = Points(i).y
  295.     NEXT
  296.  
  297. SUB reloadPoints
  298.     FOR i = 0 TO nPoints - 1
  299.         Points(i).x = CopyPts(i).x
  300.         Points(i).y = CopyPts(i).y
  301.     NEXT
  302.  
  303. FUNCTION dTrip! (a() AS pointType)
  304.     lba = LBOUND(a): uba = UBOUND(a)
  305.     FOR i = lba TO uba
  306.         IF i = uba THEN
  307.             d = d + SQR((a(i).x - a(lba).x) ^ 2 + (a(i).y - a(lba).y) ^ 2)
  308.         ELSE
  309.             d = d + SQR((a(i).x - a(i + 1).x) ^ 2 + (a(i).y - a(i + 1).y) ^ 2)
  310.         END IF
  311.     NEXT
  312.     dTrip! = d
  313.  
  314. SUB showArr (a() AS pointType, xOffset) 'does route and Points of given arr
  315.     lba = LBOUND(a): uba = UBOUND(a)
  316.     FOR i = lba TO uba
  317.         IF i = uba THEN
  318.             LINE (a(i).x + xOffset, a(i).y)-(a(lba).x + xOffset, a(lba).y)
  319.         ELSE
  320.             LINE (a(i).x + xOffset, a(i).y)-(a(i + 1).x + xOffset, a(i + 1).y)
  321.         END IF
  322.     NEXT
  323.     FOR i = LBOUND(a) TO UBOUND(a)
  324.         CIRCLE (a(i).x + xOffset, a(i).y), 2
  325.     NEXT
  326.     d = dTrip!(a())
  327.     _PRINTSTRING (200 + xOffset, ymax - 25), "Trip Distance:" + STR$(d)
  328.  
  329.  

Edit: fix variable name error in Optimize and add flag for restart.
TS #2 update.PNG
* TS #2 update.PNG (Filesize: 21.22 KB, Dimensions: 1207x635, Views: 210)
« Last Edit: April 04, 2019, 01:45:16 am by bplus »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Traveling Salesperson
« Reply #14 on: April 04, 2019, 07:30:15 pm »
A thought I’ve had on this:  Imagine an X/Y axis through the center of the screen.   Find the farthest point from that center, and rotate a line, like on a clock, either left or right, and connect the points in the order you touch them...   

That’s your route.

I don’t think permutations of travel are needed in most cases.  Just simple rotational lines from one outer point to the next.  If my time frees up a bit again soon, I’ll try and work up an example to see if it’d truly work for results like I’d think it should.

Hi Steve,

I just tested something like this today.

I took all the points on a PointsList array and found the average of either all x's and all y's or max/min of x's and y's.
This gives a middle "origin" from which to calculate all the angles of points to that center.
I then sorted the PointsList array by those angles which orders the points about the center.
It worked as shortest path ~40 to 43% of time.

Code: QB64: [Select]
  1. _TITLE "Find Shortest Trip by Angle Method" 'B+  2019-04-04
  2. '2019-03-31 #2 add thic line sub and ftri for that to show best path found so far
  3. '2019-04-01 starting with Traveling Salesperson, make the Points array 0 based and add ID to PointType.
  4. 'fix code to handle the base 0 change
  5. 'add lexicographic order code to replace shuffle, start array Order to hold the current Order being tested.
  6.  
  7. ' OK Traveling Salesman Find Shortest Rount Trip is set to run through all Permutations of Points
  8. ' to find the shortest path amoung them. (There will be nPoints * 2 minimun shortest paths
  9. ' because roundtrips are closed loops, one could start at any city and go clockwise or CCW to visit every
  10. ' city.
  11.  
  12. ' Traveling Salesperson Find Shortest Round Trip 2, save time as soon as the distance exceeds smallD move on.
  13. ' Not going to work...
  14.  
  15. ' 2019-04-02 Overhaul this to test a new approach to the finding the shortest trip.
  16. ' make a list of points
  17. ' make an array called connected and add the first 3 points of list to it, already shortest path
  18. ' start loop from 4 th point to nPoints
  19. ' get next point on list and compare distances from new point to all pairs of connected points
  20. ' insert (connect) the new point between the pair of conncted points
  21. ' loop to next get point until all points on list exhausted.
  22.  
  23. ' Save old permutations check to verify we are building smallest trips
  24. '2019-04-02 OK works but results are disappointing, need to see what is going on when adding points to build
  25.  
  26. '2019-04-02 OK build up the route from a points list by checking new point between every other point for
  27. ' shortest trip, I have not seen many crossed or intersecting lines, but running throuh all permutations
  28. ' there is often a better route, 50/50?
  29.  
  30. '2019-04-03 added Optimizer sub that takes the Connected() and runs through all the points again between
  31. 'all the other points looking for a smaller path, sometimes it finds, one 1/10?
  32.  
  33. 'BTW after half permutations run, I end checking because you wont get any better path after that.
  34. '2019-04-03 posted update
  35.  
  36. '2019-04-04  change Optimizer with a restart flag , oh man found a bug!
  37.  
  38. '2019-04-04 fork try new method of finding shortest route by Angle Method
  39. ' 1. make list of points
  40. ' 2. find middle x0, y0 (like circle origin) of either Max/Min or Average of all x and y's
  41. ' 3. calc Angle all points are to Middle X0, Y0
  42. ' 4. sort array by angle
  43. ' 5.   That is the shortst path? in what percentage?
  44. ' results
  45. '  227/598 tests about 40% when use max/min average for x, y center
  46. '  52/120 tests about 43% when use average x, y for center
  47.  
  48.  
  49.  
  50. CONST xmax = 1200
  51. CONST ymax = 600
  52. CONST nPoints = 10 '3 trivial they all are shortest!   more than 10 is agonizingly long to run all permutations
  53.  
  54. SCREEN _NEWIMAGE(xmax, ymax, 32)
  55. _SCREENMOVE 100, 40
  56.  
  57. TYPE pointType
  58.     ID AS INTEGER
  59.     x AS SINGLE
  60.     y AS SINGLE
  61.     a AS SINGLE 'angle to ave or max/min x, y
  62.  
  63. ''start of building a shortest Trip by adding one point at a time to already connected shortest path  ----------------------------
  64. DIM count AS _INTEGER64 'counting loops in the All Permutations part
  65. DIM SHARED PointsList(0 TO nPoints - 1) AS pointType
  66. DIM SHARED Points(0 TO nPoints - 1) AS pointType
  67. DIM SHARED CopyPts(nPoints - 1) AS pointType
  68. DIM SHARED Order(0 TO nPoints - 1) AS INTEGER
  69. DIM SHARED LexiDone, smallD 'signal from Lexi that it has run all perms and smallD is smallest found in All Perms section
  70.  
  71.  
  72. restart:
  73. makePointsList 'ID's will match up with Order() later
  74. showArr PointsList(), 0
  75. INPUT "Here is PointsList... press Enter to check with AllPerms.", wate$
  76. d1 = dTrip!(PointsList())
  77.  
  78. 'this is all for checking what we've built, code already works if I don't screw it up
  79. 'OK load this into Points and check if shortest path
  80. FOR i = 0 TO nPoints - 1
  81.     Points(i) = PointsList(i)
  82. copyPoints
  83.  
  84. 'load Order
  85. FOR i = 0 TO nPoints - 1
  86.     Order(i) = i
  87.  
  88. ' initialize checking
  89. nTests&& = factorial&&(nPoints)
  90. copyPoints
  91. smallD = dTrip!(Points()) ' get the distance of the points at the present load with the built path
  92. changedShortestPath = 0
  93. LexiDone = 0
  94. count = 0
  95. 'checking loop to confirm that the Connected() order is best
  96. WHILE _KEYDOWN(27) = 0 AND LexiDone = 0
  97.     'CLS
  98.     'showArr Points(), 0
  99.     'showArr CopyPts(), xmax / 2
  100.     '_PRINTSTRING (xmax / 2 + 125, 0), "Shortest Trip found systematically so far."
  101.     d = dTrip!(Points())
  102.     IF d < smallD THEN
  103.         CLS
  104.         showArr Points(), 0
  105.         showArr CopyPts(), xmax / 2
  106.         _PRINTSTRING (xmax / 2 + 125, 0), "Shortest Trip found systematically so far."
  107.         copyPoints
  108.         smallD = d
  109.         changedShortestPath = changedShortestPath + 1
  110.         _DISPLAY
  111.         _DELAY .5
  112.     END IF
  113.     count = count + 1
  114.     IF count MOD 100000 = 0 THEN
  115.         CLS
  116.         showArr Points(), 0
  117.         showArr CopyPts(), xmax / 2
  118.         _PRINTSTRING (xmax / 2 + 125, 0), "Shortest Trip found systematically so far."
  119.         LINE (0, 0)-STEP(xmax / 2 + 100, 20), _RGB32(0, 0, 0)
  120.         _PRINTSTRING (0, 0), "Round trip distance:" + STR$(d) + " for" + STR$(count) + " test of" + STR$(nTests&&) + " total tests to be made."
  121.         _DISPLAY
  122.     END IF
  123.  
  124.     '_DISPLAY
  125.     '_DELAY .01
  126.  
  127.     'Here we update the Lexicographical Order in Order array
  128.     updateOrder ' and then will swap the order of points for next test.
  129.     IF count > .5 * nTests&& THEN LexiDone = 1
  130. 'display final results
  131. PRINT "From"; count; "random routes, this was the shortest:"
  132. reloadPoints
  133. showArr Points(), 0
  134. showArr PointsList(), xmax / 2
  135. _PRINTSTRING (xmax / 2 + 200, 5), "Here is original path built:"
  136. PRINT "with distance of"; smallD
  137. PRINT "Changed shortest path:"; changedShortestPath; "times since the build of the first set of Points."
  138. PRINT "press any to Restart... "
  139.  
  140. 'just show these at end of each experiment
  141. IF smallD + .001 < d1 THEN
  142.     AllPerm = AllPerm + 1
  143.     orig = orig + 1
  144. PRINT "Original was good:"; orig
  145. PRINT "All permutation found better:"; AllPerm
  146. GOTO restart
  147.  
  148. SUB qSortOnA (start AS LONG, finish AS LONG, a() AS pointType)
  149.     DIM Hi AS LONG, Lo AS LONG, Middle AS SINGLE
  150.     Hi = finish: Lo = start
  151.     Middle = a((Lo + Hi) / 2).a 'find middle of array
  152.  
  153.     'PRINT Hi, Lo, Middle
  154.     'INPUT "Hi, Lo, Middle  enter..."; wate$
  155.     DO
  156.         DO WHILE a(Lo).a < Middle: Lo = Lo + 1: LOOP
  157.         DO WHILE a(Hi).a > Middle: Hi = Hi - 1: LOOP
  158.         IF Lo <= Hi THEN
  159.             SWAP a(Lo), a(Hi)
  160.             Lo = Lo + 1: Hi = Hi - 1
  161.         END IF
  162.     LOOP UNTIL Lo > Hi
  163.     IF Hi > start THEN qSortOnA start, Hi, a()
  164.     IF Lo < finish THEN qSortOnA Lo, finish, a()
  165.  
  166.  
  167. 'this sub runs through all the points and inserts each between all the other points looking for best distance
  168. 'but since we are far from ALL permutations this is not perfect system
  169. ' the builder code does a pretty good job connecting points for small path and this Optimizer does NOT change
  170. 'routes often.
  171. SUB Optimize (a() AS pointType)
  172.     lba = LBOUND(a): uba = UBOUND(a)
  173.     curD = dTrip!(a())
  174.     restart:
  175.     flag = 0
  176.     FOR pointer = lba TO uba
  177.         FOR p2 = lba TO uba
  178.             SWAP a(pointer), a(p2)
  179.             d = dTrip!(a())
  180.             IF d < curD THEN
  181.                 curD = d
  182.                 flag = 1
  183.             ELSE
  184.                 SWAP a(pointer), a(p2)
  185.             END IF
  186.         NEXT
  187.     NEXT
  188.     IF flag THEN GOTO restart
  189.  
  190.  
  191. FUNCTION factorial&& (n AS INTEGER)
  192.     f&& = 1
  193.     FOR i = 2 TO n
  194.         f&& = f&& * i
  195.     NEXT
  196.     factorial&& = f&&
  197.  
  198. SUB updateOrder 'from Lexicographical Order change P() to Order()
  199.     'step 1 Find largest index x st P(x) < P(x +1)
  200.  
  201.     bigX = -1 'invalid index at end says we are done
  202.     FOR x = 0 TO UBOUND(Order) - 1
  203.         IF Order(x) < Order(x + 1) THEN bigX = x
  204.     NEXT
  205.     IF bigX = -1 THEN LexiDone = 1: EXIT SUB
  206.  
  207.     'step 2 Find largest y  st P(x) < P(y)
  208.     bigY = -1
  209.     FOR y = 0 TO UBOUND(Order)
  210.         IF Order(bigX) < Order(y) THEN bigY = y
  211.     NEXT
  212.  
  213.     'step 3 swap P(bigI) , P(bigJ)
  214.     SWAP Order(bigX), Order(bigY)
  215.  
  216.     'QB64 has no slice! or any of that JS stuff, have to do this by hand
  217.     'step 4 Reverse P(x+1 ... n)   = n - (x+1) -1 elements ??? let the computer count it out!
  218.     count = 0
  219.     FOR r = bigX + 1 TO UBOUND(Order)
  220.         count = count + 1
  221.     NEXT
  222.  
  223.     REDIM temp(count)
  224.     count = 0
  225.     FOR r = UBOUND(Order) TO bigX + 1 STEP -1
  226.         temp(count) = Order(r)
  227.         count = count + 1
  228.     NEXT
  229.     count = 0
  230.     FOR r = bigX + 1 TO UBOUND(Order)
  231.         Order(r) = temp(count)
  232.         count = count + 1
  233.     NEXT
  234.  
  235.     'OK now rearrange points to Order()
  236.     FOR i = 0 TO nPoints - 2 'only have to go to secound to last
  237.         targetID = Order(i)
  238.         IF Points(i).ID <> targetID THEN 'find where it is and swap
  239.             FOR j = i + 1 TO nPoints - 1
  240.                 IF Points(j).ID = targetID THEN
  241.                     SWAP Points(i), Points(j)
  242.                     EXIT FOR
  243.                 END IF
  244.             NEXT
  245.         END IF
  246.     NEXT
  247.  
  248.  
  249. SUB makePointsList
  250.     'results of average all points 52 good and 68 found better
  251.     topx = 0: botX = 100000
  252.     topY = 0: botY = 100000
  253.     FOR i = 0 TO nPoints - 1
  254.         PointsList(i).ID = i
  255.         PointsList(i).x = RND * (xmax - 20) * .5 + 5: totX = totX + PointsList(i).x
  256.         IF PointsList(i).x < botX THEN
  257.             botX = PointsList(i).x
  258.         ELSEIF PointsList(i).x > topx THEN
  259.             topx = PointsList(i).x
  260.         END IF
  261.         PointsList(i).y = RND * (ymax - 40) + 40: totY = totY + PointsList(i).y
  262.         IF PointsList(i).y < botY THEN
  263.             botY = PointsList(i).y
  264.         ELSEIF PointsList(i).y > topY THEN
  265.             topY = PointsList(i).y
  266.         END IF
  267.     NEXT
  268.  
  269.     'average  of all points
  270.     'x0 = totX / nPoints
  271.     'y0 = totY / nPoints
  272.  
  273.     'or averge of extreme points?
  274.     x0 = (topx + botX) / 2
  275.     y0 = (topY + botY) / 2
  276.     FOR i = 0 TO nPoints - 1
  277.         PointsList(i).a = _ATAN2(PointsList(i).y - y0, PointsList(i).x - x0)
  278.     NEXT
  279.     qSortOnA 0, nPoints - 1, PointsList()
  280.  
  281. SUB copyPoints
  282.     FOR i = 0 TO nPoints - 1
  283.         CopyPts(i).x = Points(i).x
  284.         CopyPts(i).y = Points(i).y
  285.     NEXT
  286.  
  287. SUB reloadPoints
  288.     FOR i = 0 TO nPoints - 1
  289.         Points(i).x = CopyPts(i).x
  290.         Points(i).y = CopyPts(i).y
  291.     NEXT
  292.  
  293. FUNCTION dTrip! (a() AS pointType)
  294.     lba = LBOUND(a): uba = UBOUND(a)
  295.     FOR i = lba TO uba
  296.         IF i = uba THEN
  297.             d = d + SQR((a(i).x - a(lba).x) ^ 2 + (a(i).y - a(lba).y) ^ 2)
  298.         ELSE
  299.             d = d + SQR((a(i).x - a(i + 1).x) ^ 2 + (a(i).y - a(i + 1).y) ^ 2)
  300.         END IF
  301.     NEXT
  302.     dTrip! = d
  303.  
  304. SUB showArr (a() AS pointType, xOffset) 'does route and Points of given arr
  305.     lba = LBOUND(a): uba = UBOUND(a)
  306.     FOR i = lba TO uba
  307.         IF i = uba THEN
  308.             LINE (a(i).x + xOffset, a(i).y)-(a(lba).x + xOffset, a(lba).y)
  309.         ELSE
  310.             LINE (a(i).x + xOffset, a(i).y)-(a(i + 1).x + xOffset, a(i + 1).y)
  311.         END IF
  312.     NEXT
  313.     FOR i = LBOUND(a) TO UBOUND(a)
  314.         CIRCLE (a(i).x + xOffset, a(i).y), 2
  315.     NEXT
  316.     d = dTrip!(a())
  317.     _PRINTSTRING (200 + xOffset, ymax - 25), "Trip Distance:" + STR$(d)
  318.  
  319.