tsp_local.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import sys
  2. import copy
  3. import math
  4. from heapq import heappush, heappop
  5. from secrets import randbelow
  6. from random import shuffle, sample
  7. distances = []
  8. n = 0
  9. # returns a shuffled version of the cities list while maintaining the first & last elements
  10. def rand_order(cities):
  11. # shitty variable name
  12. suburbs = cities[1:-1]
  13. shuffle(suburbs)
  14. suburbs.insert(0, cities[0])
  15. suburbs.append(cities[-1])
  16. return suburbs
  17. # returns distance between 2 vertices
  18. def dist(city1, city2):
  19. return math.sqrt((city1[2] - city2[2]) ** 2 + (city1[3] - city2[3]) ** 2)
  20. # returns total cost/distance of a tour (list of cities)
  21. def length(cities):
  22. sum = 0
  23. for i in range(len(cities) - 1):
  24. sum += dist(cities[i], cities[i+1])
  25. return sum
  26. # returns the best ordered child state of a state (hill climbing, basic) & its cost
  27. def best_child(cities, cost):
  28. bestorder = cities
  29. bestcost = cost
  30. for i in range(1, len(cities) - 2):
  31. for j in range(i, len(cities) - 1):
  32. temporder = copy.deepcopy(cities)
  33. tempcity = temporder[i]
  34. temporder[i] = temporder[j]
  35. temporder[j] = tempcity
  36. templen = length(temporder)
  37. if (templen < cost):
  38. bestorder = temporder
  39. bestcost = templen
  40. return (bestorder, bestcost)
  41. # returns the best child using tabu
  42. # returns the best ordered child state of a state (hill climbing, basic) & its cost
  43. def bester_child(cities, cost, tabu):
  44. bestorder = cities
  45. bestcost = cost
  46. for i in range(1, len(cities) - 2):
  47. for j in range(i, len(cities) - 1):
  48. temporder = copy.deepcopy(cities)
  49. tempcity = temporder[i]
  50. temporder[i] = temporder[j]
  51. temporder[j] = tempcity
  52. if (get_hashable(temporder) in tabu):
  53. continue
  54. templen = length(temporder)
  55. if (templen < cost):
  56. bestorder = temporder
  57. bestcost = templen
  58. return (bestorder, bestcost)
  59. # returns a random child for simulated annealing search
  60. # returns the ordering and the cost of it
  61. def random_child(cities):
  62. i = randbelow(len(cities) - 3) + 1
  63. j = i
  64. while (j == i):
  65. j = randbelow(len(cities) - 3) + 1
  66. temporder = copy.deepcopy(cities)
  67. tempcity = temporder[i]
  68. temporder[i] = temporder[j]
  69. temporder[j] = tempcity
  70. return(temporder, length(temporder))
  71. # returns the basic hill climbing solution for a given input
  72. def sir_edmund_hillary(cities):
  73. curcost = length(cities)
  74. curpath = copy.deepcopy(cities)
  75. # keep trying things til we can no longer try things
  76. while(1):
  77. child = best_child(curpath, curcost)
  78. if (child[1] < curcost):
  79. curcost = child[1]
  80. curpath = child[0]
  81. continue
  82. break
  83. return(curpath, curcost)
  84. # retuns a tuple of the indices (ordered) in a list of cities
  85. def get_hashable(cities):
  86. newlist = []
  87. for i in range(1, len(cities) - 1):
  88. newlist.append(cities[i][1])
  89. return tuple(newlist)
  90. # returns the hill climbing solution w/tabu & sideways moves for a given input
  91. def tenzing_norgay(cities):
  92. curcost = length(cities)
  93. curpath = copy.deepcopy(cities)
  94. tabu = {get_hashable(cities)}
  95. laterals = 0
  96. # keep trying things til we can no longer try things
  97. while(1):
  98. child = bester_child(curpath, curcost, tabu)
  99. if (child[1] < curcost):
  100. curcost = child[1]
  101. curpath = child[0]
  102. tabu.add(get_hashable(curpath))
  103. continue
  104. if (child[1] == curcost and laterals < n):
  105. laterals += 1
  106. curcost = child[1]
  107. curpath = child[0]
  108. tabu.add(get_hashable(curpath))
  109. continue
  110. break
  111. return(curpath, curcost)
  112. # returns the hill climbing solution w/ x random restarts for a given input
  113. def reinhold_messner(cities, x):
  114. curcost = length(cities)
  115. curpath = copy.deepcopy(cities)
  116. bestcost = length(cities)
  117. bestpath = copy.deepcopy(cities)
  118. # keep trying things til we can no longer try things
  119. while(x):
  120. child = best_child(curpath, curcost)
  121. if (child[1] < curcost):
  122. curcost = child[1]
  123. curpath = child[0]
  124. continue
  125. else:
  126. if (curcost < bestcost):
  127. bestcost = curcost
  128. bestpath = copy.deepcopy(curpath)
  129. curpath = rand_order(cities)
  130. curcost = length(curpath)
  131. x -= 1
  132. return(bestpath, bestcost)
  133. # returns true or false randomly, based on the distribution e^(delta / temp)
  134. def bad_weather(delta, temp):
  135. return secrets.SystemRandom().random() < math.exp(delta / temp)
  136. # decrements temp by the option specified by opt: 1 is linear, 2 is logarithmic, 3 is exponential
  137. def colder(temp, opt):
  138. if (opt == 1):
  139. return temp - 25
  140. if (opt == 2):
  141. return temp - (math.log(temp) + 2)
  142. else:
  143. return temp - math.exp(-0.001 * temp)
  144. # returns the hill climbing solution using simulated annealing w/ schedule opt
  145. def beck_weathers(cities, opt):
  146. temp = 1000
  147. curcost = length(cities)
  148. curpath = copy.deepcopy(cities)
  149. # keep trying things til we can no longer try things
  150. while(temp > 0):
  151. child = random_child(curpath)
  152. delta = curcost - child[1]
  153. if (delta > 0 or bad_weather(delta, temp)):
  154. curcost = child[1]
  155. curpath = child[0]
  156. temp = colder(temp, opt)
  157. return(curpath, curcost)
  158. # solves the inputted TSP problem
  159. def solver(problem):
  160. # import map
  161. prob = problem
  162. with open(prob) as file:
  163. prob = file.read().splitlines()
  164. global n
  165. n = int(prob[0])
  166. cities = prob[1:]
  167. counter = 0
  168. # convert to list of list of ints
  169. # original format is: letter, coord, coord
  170. # output format is: iD#, letter, coord, coord
  171. for l in cities:
  172. temp = l.split()
  173. temp[1] = int(temp[1])
  174. temp[2] = int(temp[2])
  175. temp.insert(0, counter)
  176. counter += 1
  177. cities[cities.index(l)] = temp
  178. # add in goal state
  179. cities.append(cities[0])
  180. #print(solve(frontier, cities))
  181. print(cities)
  182. #print(randbelow(n - 1) + 1)
  183. print("#####")
  184. input = rand_order(cities)
  185. print(input)
  186. print("#####")
  187. #result = reinhold_messner(input, 20)
  188. result = beck_weathers(input, 1)
  189. print(result[0])
  190. print(result[1])
  191. def main():
  192. solver("instance_10.txt")
  193. main()