|
@@ -0,0 +1,246 @@
|
|
|
+import sys
|
|
|
+import copy
|
|
|
+import math
|
|
|
+from heapq import heappush, heappop
|
|
|
+from secrets import randbelow
|
|
|
+from random import shuffle, sample
|
|
|
+
|
|
|
+distances = []
|
|
|
+n = 0
|
|
|
+
|
|
|
+# returns a shuffled version of the cities list while maintaining the first & last elements
|
|
|
+def rand_order(cities):
|
|
|
+ # shitty variable name
|
|
|
+ suburbs = cities[1:-1]
|
|
|
+ shuffle(suburbs)
|
|
|
+ suburbs.insert(0, cities[0])
|
|
|
+ suburbs.append(cities[-1])
|
|
|
+ return suburbs
|
|
|
+
|
|
|
+
|
|
|
+# returns distance between 2 vertices
|
|
|
+def dist(city1, city2):
|
|
|
+ return math.sqrt((city1[2] - city2[2]) ** 2 + (city1[3] - city2[3]) ** 2)
|
|
|
+
|
|
|
+
|
|
|
+# returns total cost/distance of a tour (list of cities)
|
|
|
+def length(cities):
|
|
|
+ sum = 0
|
|
|
+ for i in range(len(cities) - 1):
|
|
|
+ sum += dist(cities[i], cities[i+1])
|
|
|
+
|
|
|
+ return sum
|
|
|
+
|
|
|
+
|
|
|
+# returns the best ordered child state of a state (hill climbing, basic) & its cost
|
|
|
+def best_child(cities, cost):
|
|
|
+ bestorder = cities
|
|
|
+ bestcost = cost
|
|
|
+
|
|
|
+ for i in range(1, len(cities) - 2):
|
|
|
+ for j in range(i, len(cities) - 1):
|
|
|
+ temporder = copy.deepcopy(cities)
|
|
|
+ tempcity = temporder[i]
|
|
|
+ temporder[i] = temporder[j]
|
|
|
+ temporder[j] = tempcity
|
|
|
+ templen = length(temporder)
|
|
|
+ if (templen < cost):
|
|
|
+ bestorder = temporder
|
|
|
+ bestcost = templen
|
|
|
+
|
|
|
+ return (bestorder, bestcost)
|
|
|
+
|
|
|
+
|
|
|
+# returns the best child using tabu
|
|
|
+# returns the best ordered child state of a state (hill climbing, basic) & its cost
|
|
|
+def bester_child(cities, cost, tabu):
|
|
|
+ bestorder = cities
|
|
|
+ bestcost = cost
|
|
|
+
|
|
|
+ for i in range(1, len(cities) - 2):
|
|
|
+ for j in range(i, len(cities) - 1):
|
|
|
+ temporder = copy.deepcopy(cities)
|
|
|
+ tempcity = temporder[i]
|
|
|
+ temporder[i] = temporder[j]
|
|
|
+ temporder[j] = tempcity
|
|
|
+
|
|
|
+ if (get_hashable(temporder) in tabu):
|
|
|
+ continue
|
|
|
+
|
|
|
+ templen = length(temporder)
|
|
|
+ if (templen < cost):
|
|
|
+ bestorder = temporder
|
|
|
+ bestcost = templen
|
|
|
+
|
|
|
+ return (bestorder, bestcost)
|
|
|
+
|
|
|
+
|
|
|
+# returns a random child for simulated annealing search
|
|
|
+# returns the ordering and the cost of it
|
|
|
+def random_child(cities):
|
|
|
+ i = randbelow(len(cities) - 3) + 1
|
|
|
+ j = i
|
|
|
+
|
|
|
+ while (j == i):
|
|
|
+ j = randbelow(len(cities) - 3) + 1
|
|
|
+
|
|
|
+ temporder = copy.deepcopy(cities)
|
|
|
+ tempcity = temporder[i]
|
|
|
+ temporder[i] = temporder[j]
|
|
|
+ temporder[j] = tempcity
|
|
|
+
|
|
|
+ return(temporder, length(temporder))
|
|
|
+
|
|
|
+
|
|
|
+# returns the basic hill climbing solution for a given input
|
|
|
+def sir_edmund_hillary(cities):
|
|
|
+ curcost = length(cities)
|
|
|
+ curpath = copy.deepcopy(cities)
|
|
|
+
|
|
|
+ # keep trying things til we can no longer try things
|
|
|
+ while(1):
|
|
|
+ child = best_child(curpath, curcost)
|
|
|
+ if (child[1] < curcost):
|
|
|
+ curcost = child[1]
|
|
|
+ curpath = child[0]
|
|
|
+ continue
|
|
|
+ break
|
|
|
+
|
|
|
+ return(curpath, curcost)
|
|
|
+
|
|
|
+
|
|
|
+# retuns a tuple of the indices (ordered) in a list of cities
|
|
|
+def get_hashable(cities):
|
|
|
+ newlist = []
|
|
|
+ for i in range(1, len(cities) - 1):
|
|
|
+ newlist.append(cities[i][1])
|
|
|
+
|
|
|
+ return tuple(newlist)
|
|
|
+
|
|
|
+
|
|
|
+# returns the hill climbing solution w/tabu & sideways moves for a given input
|
|
|
+def tenzing_norgay(cities):
|
|
|
+ curcost = length(cities)
|
|
|
+ curpath = copy.deepcopy(cities)
|
|
|
+ tabu = {get_hashable(cities)}
|
|
|
+ laterals = 0
|
|
|
+
|
|
|
+ # keep trying things til we can no longer try things
|
|
|
+ while(1):
|
|
|
+ child = bester_child(curpath, curcost, tabu)
|
|
|
+ if (child[1] < curcost):
|
|
|
+ curcost = child[1]
|
|
|
+ curpath = child[0]
|
|
|
+ tabu.add(get_hashable(curpath))
|
|
|
+ continue
|
|
|
+ if (child[1] == curcost and laterals < n):
|
|
|
+ laterals += 1
|
|
|
+ curcost = child[1]
|
|
|
+ curpath = child[0]
|
|
|
+ tabu.add(get_hashable(curpath))
|
|
|
+ continue
|
|
|
+ break
|
|
|
+
|
|
|
+ return(curpath, curcost)
|
|
|
+
|
|
|
+
|
|
|
+# returns the hill climbing solution w/ x random restarts for a given input
|
|
|
+def reinhold_messner(cities, x):
|
|
|
+ curcost = length(cities)
|
|
|
+ curpath = copy.deepcopy(cities)
|
|
|
+ bestcost = length(cities)
|
|
|
+ bestpath = copy.deepcopy(cities)
|
|
|
+
|
|
|
+ # keep trying things til we can no longer try things
|
|
|
+ while(x):
|
|
|
+ child = best_child(curpath, curcost)
|
|
|
+ if (child[1] < curcost):
|
|
|
+ curcost = child[1]
|
|
|
+ curpath = child[0]
|
|
|
+ continue
|
|
|
+ else:
|
|
|
+ if (curcost < bestcost):
|
|
|
+ bestcost = curcost
|
|
|
+ bestpath = copy.deepcopy(curpath)
|
|
|
+ curpath = rand_order(cities)
|
|
|
+ curcost = length(curpath)
|
|
|
+ x -= 1
|
|
|
+
|
|
|
+ return(bestpath, bestcost)
|
|
|
+
|
|
|
+
|
|
|
+# returns true or false randomly, based on the distribution e^(delta / temp)
|
|
|
+def bad_weather(delta, temp):
|
|
|
+ return secrets.SystemRandom().random() < math.exp(delta / temp)
|
|
|
+
|
|
|
+
|
|
|
+# decrements temp by the option specified by opt: 1 is linear, 2 is logarithmic, 3 is exponential
|
|
|
+def colder(temp, opt):
|
|
|
+ if (opt == 1):
|
|
|
+ return temp - 25
|
|
|
+ if (opt == 2):
|
|
|
+ return temp - (math.log(temp) + 2)
|
|
|
+ else:
|
|
|
+ return temp - math.exp(-0.001 * temp)
|
|
|
+
|
|
|
+
|
|
|
+# returns the hill climbing solution using simulated annealing w/ schedule opt
|
|
|
+def beck_weathers(cities, opt):
|
|
|
+ temp = 1000
|
|
|
+ curcost = length(cities)
|
|
|
+ curpath = copy.deepcopy(cities)
|
|
|
+
|
|
|
+ # keep trying things til we can no longer try things
|
|
|
+ while(temp > 0):
|
|
|
+ child = random_child(curpath)
|
|
|
+ delta = curcost - child[1]
|
|
|
+ if (delta > 0 or bad_weather(delta, temp)):
|
|
|
+ curcost = child[1]
|
|
|
+ curpath = child[0]
|
|
|
+ temp = colder(temp, opt)
|
|
|
+
|
|
|
+ return(curpath, curcost)
|
|
|
+
|
|
|
+
|
|
|
+# solves the inputted TSP problem
|
|
|
+def solver(problem):
|
|
|
+ # import map
|
|
|
+ prob = problem
|
|
|
+ with open(prob) as file:
|
|
|
+ prob = file.read().splitlines()
|
|
|
+
|
|
|
+ global n
|
|
|
+ n = int(prob[0])
|
|
|
+ cities = prob[1:]
|
|
|
+ counter = 0
|
|
|
+ # convert to list of list of ints
|
|
|
+ # original format is: letter, coord, coord
|
|
|
+ # output format is: iD#, letter, coord, coord
|
|
|
+ for l in cities:
|
|
|
+ temp = l.split()
|
|
|
+ temp[1] = int(temp[1])
|
|
|
+ temp[2] = int(temp[2])
|
|
|
+ temp.insert(0, counter)
|
|
|
+ counter += 1
|
|
|
+ cities[cities.index(l)] = temp
|
|
|
+
|
|
|
+ # add in goal state
|
|
|
+ cities.append(cities[0])
|
|
|
+
|
|
|
+ #print(solve(frontier, cities))
|
|
|
+ print(cities)
|
|
|
+ #print(randbelow(n - 1) + 1)
|
|
|
+ print("#####")
|
|
|
+ input = rand_order(cities)
|
|
|
+ print(input)
|
|
|
+ print("#####")
|
|
|
+ #result = reinhold_messner(input, 20)
|
|
|
+ result = beck_weathers(input, 1)
|
|
|
+ print(result[0])
|
|
|
+ print(result[1])
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ solver("instance_10.txt")
|
|
|
+
|
|
|
+main()
|