def get_best_path(digraph, start, end, path, max_dist_outdoors, best_dist, best_path): """ Finds the shortest path between buildings. Returns: A tuple with the shortest-path from start to end, represented by a list of building numbers and the distance of that path. If there exists no path that satisfies max_total_dist and max_dist_outdoors constraints, then return None. """ start = Node(start) end = Node(end) path[0].append(start.get_name()) if start not in digraph.nodes or end not in digraph.nodes: raise ValueError elif start == end: return tuple([path[0].copy(), path[1]]) else: for edge in digraph.edges[start]: if edge.get_destination().get_name() not in path[0]: if len(best_path) == 0 or len(path[0]) < len(best_path): if path[2] + edge.get_outdoor_distance( ) <= max_dist_outdoors: path[1] += edge.get_total_distance() path[2] += edge.get_outdoor_distance() next_path = get_best_path(digraph, edge.get_destination(), end, path, max_dist_outdoors, best_dist, best_path) path[0].remove(edge.get_destination().get_name()) path[1] -= edge.get_total_distance() path[2] -= edge.get_outdoor_distance() else: continue if next_path is not None: if best_dist == 0 or next_path[1] < best_dist: best_path = next_path[0] best_dist = next_path[1] if best_dist == 0: return None return tuple([best_path, best_dist])
def directed_dfs(digraph, start, end, max_total_dist, max_dist_outdoors): """ Finds the shortest path from start to end using a directed depth-first search. The total distance traveled on the path must not exceed max_total_dist, and the distance spent outdoors on this path must not exceed max_dist_outdoors. Parameters: digraph: Digraph instance The graph on which to carry out the search start: string Building number at which to start end: string Building number at which to end max_total_dist: int Maximum total distance on a path max_dist_outdoors: int Maximum distance spent outdoors on a path Returns: The shortest-path from start to end, represented by a list of building numbers (in strings), [n_1, n_2, ..., n_k], where there exists an edge from n_i to n_(i+1) in digraph, for all 1 <= i < k If there exists no path that satisfies max_total_dist and max_dist_outdoors constraints, then raises a ValueError. """ global Best_dist Best_dist = 99999 start = Node(start) end = Node(end) if (not digraph.has_node(start)) or (not digraph.has_node(end)): raise ValueError("Bad test case!") path = [[start.get_name()], 0, 0] best_path = [] ans = get_best_path(digraph, start, end, path, max_dist_outdoors, max_total_dist, best_path) if best_path == None or len(best_path) == 0: raise ValueError("Bad test case!") return list(ans)
def get_best_path(digraph, start, end, path, max_dist_outdoors, best_dist, best_path): """ Finds the shortest path between buildings subject to constraints. Parameters: digraph: Digraph instance The graph on which to carry out the search start: string Building number at which to start end: string Building number at which to end path: list composed of [[list of strings], int, int] Represents the current path of nodes being traversed. Contains a list of node names, total distance traveled, and total distance outdoors. max_dist_outdoors: int Maximum distance spent outdoors on a path best_dist: int The smallest distance between the original start and end node for the initial problem that you are trying to solve best_path: list of strings The shortest path found so far between the original start and end node. Returns: A tuple with the shortest-path from start to end, represented by a list of building numbers (in strings), [n_1, n_2, ..., n_k], where there exists an edge from n_i to n_(i+1) in digraph, for all 1 <= i < k and the distance of that path. If there exists no path that satisfies max_total_dist and max_dist_outdoors constraints, then return None. """ global edge path1 = path.copy() path1[0] = path[0].copy() start = Node(start) ## print(start) ## print(end) end = Node(end) if digraph.has_node(start) == False: raise ValueError("Node not in digraph") elif digraph.has_node(end) == False: raise ValueError("Node not in digraph") if len(path1[0]) != 0: path1[2] += int(edge.get_outdoor_distance()) path1[1] += int(edge.get_total_distance()) path1[0] = path1[0] + [str(edge.get_destination())] else: path1[0] = path1[0] + [str(start.get_name())] ## print('start, ', path) if start == end and path1[2] <= max_dist_outdoors: ## print('start == end', start) return path1 r = digraph.get_edges_for_node(start) list1 = [] for pedge in r: list1.append(pedge.get_destination()) ## print('edges', list1) for edge in digraph.get_edges_for_node(start): ## print('edge', edge) start_node = str(edge.get_destination()) ## print('start2', start_node) ## print('TEST', path1) if start_node not in path1[0]: if best_path == None or (path1[1] < best_dist and path1[2] <= max_dist_outdoors): ## print('ent1') new_path = get_best_path(digraph, start_node, end, path1, max_dist_outdoors, best_dist, best_path) ## print('new', new_path) if new_path != None: if (new_path[2] <= max_dist_outdoors and new_path[1] <= best_dist) or best_dist == 0: ## print('ent2') best_dist = new_path[1] best_path = new_path.copy() ## print('best path: ', best_path) ## print(' ') ## if str(edge.get_destination()) in path[0]: ## ## path[2] -= int(edge.get_outdoor_distance()) ## path[1] -= int(edge.get_total_distance()) ## path[0].remove(str(edge.get_destination())) return best_path
def get_best_path(digraph, start, end, path, max_dist_outdoors, best_dist, best_path): """ Finds the shortest path between buildings subject to constraints. Parameters: digraph: Digraph instance The graph on which to carry out the search start: string Building number at which to start end: string Building number at which to end path: list composed of [[list of strings], int, int] Represents the current path of nodes being traversed. Contains a list of node names, total distance traveled, and total distance outdoors. max_dist_outdoors: int Maximum distance spent outdoors on a path best_dist: int The smallest distance between the original start and end node for the initial problem that you are trying to solve best_path: list of strings The shortest path found so far between the original start and end node. Returns: A tuple with the shortest-path from start to end, represented by a list of building numbers (in strings), [n_1, n_2, ..., n_k], where there exists an edge from n_i to n_(i+1) in digraph, for all 1 <= i < k and the distance of that path. If there exists no path that satisfies max_total_dist and max_dist_outdoors constraints, then return None. """ start = Node(start) end = Node(end) path = path + [start] # print("path", path) if start == end: # print("s=e") return path # nodes = digraph.get_edges_for_node(start) # print("nodes:", end=" ") # for n in nodes: # print(n, end=", ") # print("done") for node in digraph.get_edges_for_node(start): # print("node:", node) if node.get_destination() not in path: # print(node.get_destination(), "is node not in path") newPath = get_best_path(digraph, node.get_destination().get_name(), end.get_name(), path, max_dist_outdoors, best_dist, best_path) # print("new Path:", newPath) if newPath is not None: current_distance_outdoors = distance_outdoors(digraph, newPath) current_distance_total = current_distance(digraph, newPath) # print("current_distance_outdoors:", current_distance_outdoors) # print("current_distance:", current_distance_total) if (best_dist == 0) or ( (current_distance_outdoors <= max_dist_outdoors) and (current_distance_total <= best_dist)): # print("IN") best_dist = current_distance_total best_path = newPath # print("best_dist:", best_dist) # print("best_path:", best_path) # print("best_distance:", best_dist) if len(best_path) == 0: return None else: return best_path
def get_best_path(digraph, start, end, path, max_dist_outdoors, best_dist, best_path): """ Finds the shortest path between buildings subject to constraints. Parameters: digraph: Digraph instance The graph on which to carry out the search start: string Building number at which to start end: string Building number at which to end path: list composed of [[list of strings], int, int] Represents the current path of nodes being traversed. Contains a list of node names, total distance traveled, and total distance outdoors. max_dist_outdoors: int Maximum distance spent outdoors on a path best_dist: int The smallest distance between the original start and end node for the initial problem that you are trying to solve best_path: list of strings The shortest path found so far between the original start and end node. Returns: A tuple with the shortest-path from start to end, represented by a list of building numbers (in strings), [n_1, n_2, ..., n_k], where there exists an edge from n_i to n_(i+1) in digraph, for all 1 <= i < k and the distance of that path. If there exists no path that satisfies max_total_dist and max_dist_outdoors constraints, then return None. """ # TODO #use dynamic programming and recursion #return none if all possible choice exceeds max_dist of max_outdoor_dist #update path path[0] = path[0] + [start] #Initialize Start and End both as Nodes start_n = Node(start) end_n = Node(end) if not digraph.has_node(start_n) or not digraph.has_node(end_n): raise ValueError('invalid start or end node') elif start == end: #when reaches destination is when recusion should stop return (path[0], path[1]) else: for edge in digraph.get_edges_for_node(start_n): next_node = Node(edge.get_destination()) next_node_tot_dist = edge.get_total_distance() next_node_out_dist = edge.get_outdoor_distance() #get next node, total dist, and outdoor dist #make sure doesn't cycle i.e. come back to same node if next_node.get_name() not in path[0]: #check if max outdoor dist exceeded if next_node_out_dist + path[2] <= max_dist_outdoors: #only have to keep going if path is going to be shorter than current best path if best_path == None or next_node_tot_dist + path[ 1] < best_dist: path_update = [ path[0].copy(), path[1] + next_node_tot_dist, path[2] + next_node_out_dist ] #updating original path with [list of node, total distance travelled, total dist outdoors] #note that path[0] not updated here but rather within recursive function due to conderation of starting conditions #the "start" parameter of get_best_path function is str, not Node object. next_path = get_best_path(digraph, next_node.get_name(), end, path_update, max_dist_outdoors, best_dist, best_path) #if all condition satisfied, traverse to next node if next_path != None: best_path = next_path best_dist = next_path[1] return best_path