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()