def least_cost_path(graph, start, dest, cost): reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance( (edge[1], nbr))) if dest not in reached: return [] current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] return path
def least_cost_path(graph, start, dest, cost): reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance((edge[1], nbr))) # if the dest is not in reached, then no route was found if dest not in reached: return [] current = dest route = [current] # go through the reached vertices until we get back to start and append # each vertice that we "stop" at while current != start: current = reached[current] route.append(current) # reverse the list because we made a list that went from the dest to start route = route[::-1] return route
def make_valid_heaps(): valid_heaps = [] for permutation in unsorted_lists: heap = BinaryHeap() for item in permutation: heap.insert(item) valid_heaps.append(heap) return valid_heaps
def least_cost_path(graph, start, dest, location): """ *** NOTE: This function is a modified version of our previous *** *** implementation of Dijkstra's Algorithm from Assignment 1 *** *** Part 1. Comments in code indicate changes made. *** Find and return a least cost path in graph from start vertex to dest vertex. Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. location: A dictionary containing all the vertices on the screen. Keys of location are identifiers holding the vertices as values. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} events = BinaryHeap() events.insert((start, start), 0) while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] # each vertex in our graph is a tuple, so we use tuples to find neighbours # instead of single values to find the neighbours of a vertex for nbr in graph.neighbours(location[edge[1]]): # find the identifier/key value attached to nbr tuple nbrID = neighbour_identifier(nbr, location) # insert to binary heap appropriately events.insert((edge[1], nbrID), time + distance(location[edge[1]], nbr)) # FIND MINIMUM PATH IF POSSIBLE if dest not in reached: return [] # start at the dest and continously ind the parent of current vertex # until have reached starting vertex current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] # reverse the list so starts from the ghost return path
def least_cost_path_lrt(self, lrts, end): reached = {} events = BinaryHeap() #insert start location with time of 0 into the heap counter = 0 for key, val in lrts.items(): events.insert((val, val, 0), 0) while (len(events)) > 0: pair, time = events.popmin() #print(time) #account for heuristic so it is not compounded if (pair[2]): time -= pair[2] if pair[1] not in reached: # add to reached dictionary reached[pair[1]] = pair[0] #consider neighbors around each point and distance to each neighbor for neighbour in self.graph.neighbours(pair[1]): point1 = self.locations[pair[1]] point2 = self.locations[neighbour] # maybe we could save distances between objects in a dict, and reuse them? # add heuristic in for the form of euclidean distance. This will helps lead the searching algorithm # more towards the end location heuristic = euclidean_distance(point2, self.locations[end]) # add key and val to BinaryHeap called events events.insert( (pair[1], neighbour, heuristic), time + euclidean_distance(point1, point2) + heuristic) # end loop right after destination is popped if (pair[1] == end): break # use the get_path included with the breadth_first_search helper file to find path from reached dictionary path = None closestLRT = None for key, val in lrts.items(): if val in reached: path = get_path(reached, val, end) if path == None: pass else: closestLRT = key break return path, closestLRT
def test_empty_heap_insert_extract_elements_should_return_sorted_elements(self): nums = [3, 4, -1, 15, 2, 77, -3, 4, 12] heap = BinaryHeap() expected = [77, 15, 12, 4, 4, 3, 2, -1, -3] result = [] for num in nums: heap.insert(num) while heap.count > 0: result.append(heap.extract_max()) self.assertEqual(result, expected)
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] # burn vertex v, record predecessor u for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance((edge[1], nbr))) # if the dest is not in the reached dictionary, then return an empty list if dest not in reached: return [] # IMPLEMENTED FROM GET_PATH FUNCTION FROM breadth_first_search # finds minimum path # start at the dest and continuosly and find the parent of current vertex # until have reached starting vertex current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] return path
class Astar: def __init__(self, initial_state, heuristic, weight=1): self.expansions = 0 self.generated = 0 self.initial_state = initial_state self.weight = weight self.heuristic = heuristic def search(self): self.start_time = time.process_time() self.open = BinaryHeap() self.expansions = 0 initial_node = Node(self.initial_state) initial_node.g = 0 initial_node.h = self.heuristic(self.initial_state) initial_node.key = 10000 * self.weight * initial_node.h # asignamos el valor f self.open.insert(initial_node) # para cada estado alguna vez generado, generated almacena # el Node que le corresponde self.generated = {} self.generated[self.initial_state] = initial_node while not self.open.is_empty(): n = self.open.extract() # extrae n de la open if n.state.is_goal(): self.end_time = time.process_time() return n succ = n.state.successors() self.expansions += 1 for child_state, action, cost in succ: child_node = self.generated.get(child_state) is_new = child_node is None # es la primera vez que veo a child_state path_cost = n.g + cost # costo del camino encontrado hasta child_state if is_new or path_cost < child_node.g: # si vemos el estado child_state por primera vez o lo vemos por # un mejor camino, entonces lo agregamos a open if is_new: # creamos el nodo de child_state child_node = Node(child_state, n) child_node.h = self.heuristic(child_state) self.generated[child_state] = child_node child_node.action = action child_node.parent = n child_node.g = path_cost child_node.key = 10000 * ( child_node.g + self.weight * child_node.h ) - child_node.g # actualizamos el f de child_node self.open.insert( child_node ) # inserta child_node a la open si no esta en la open self.end_time = time.process_time( ) # en caso contrario, modifica la posicion de child_node en open return None
class PriorityQueue: def __init__(self): self.binary_heap = BinaryHeap(lambda a, b: a[1] < b[1]) def insert(self, element): self.binary_heap.insert(element) def multiple_insert(self, elements): for element in elements: self.insert(element) def extract(self): element = self.binary_heap.extract() return element[0] if element else element
def least_cost_path(graph, start, dest, cost): # Implementation of Dijkstra's algorithm events = BinaryHeap() reached = dict() events.insert((start, start), 0) # Begin at time 0, at the start vertex while events: edge, time = events.popmin() # Get next burnt vertex if edge[1] not in reached: # If the destination is not been reached reached[edge[1]] = edge[0] # Keep track of where we came from for w in graph.neighbours(edge[1]): # Burn the neighbours!!!! events.insert(((edge[1]), w), (time + cost.distance( (edge[1], w)))) # Add the fuse return get_path(reached, start, dest) # return the path
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance( (edge[1], nbr))) # if the dest is not in reached, then no route was found if dest not in reached: return [] current = dest route = [current] # go through the reached vertices until we get back to start and append # each vertice that we "stop" at while current != start: current = reached[current] route.append(current) # reverse the list because we made a list that went from the dest to start route = route[::-1] return route
def least_cost_path(graph, start, dest, cost): """ Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ # initialize reached as a empty dict reached = {} # an instance of BinaryHeap class, initially empty events = BinaryHeap() events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: (u, v), time = events.popmin() if v not in reached: reached[v] = u # burn vertex v, record predecessor u nbr_list = graph.neighbours(v) for w in nbr_list: # new event: edge (v,w) started burning events.insert((v, w), time + cost.distance((v, w))) # using the get_path function from bfs file developed in class # get the path as a list, if no path then a empty list return get_path(reached, start, dest)
class PriorityQueue(object): def __init__(self): self._heap = BinaryHeap() def peek(self): return self._heap.peek_min() def is_empty(self): return self._heap.is_empty() def enqueue(self, value): self._heap.insert(value) def dequeue(self): self._heap.extract_min() def __iter__(self): yield from iter(self._heap) def __len__(self): return len(self._heap)
class TestBinaryHeap(unittest.TestCase): def setUp(self): size = 8 self.random_list = random.sample(range(0, size), size) print "random list generated: " + str(self.random_list) self.heap = BinaryHeap(size) for key in self.random_list: if not self.heap.is_full(): self.heap.insert(key) def tearDown(self): pass def test_delete_min(self): order_list = sorted(self.random_list) index = 0 while not self.heap.is_empty(): min_value = self.heap.delete_min() print min_value self.assertEqual(min_value, order_list[index]) index += 1
def test_insert(self): bh = BinaryHeap(compare=lambda a, b: a < b) bh.insert(3) bh.insert(3) bh.insert(2) self.assertSequenceEqual([3, 3, 2], bh.values) bh.insert(4) self.assertSequenceEqual([4, 3, 2, 3], bh.values)
def test_insert_nodes_with_descending_values(self): binary_heap = BinaryHeap() binary_heap.insert(HeapNode({'name': 'A', 'value': 0.91}, 'value')) self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [0.91]) binary_heap.insert(HeapNode({'name': 'B', 'value': 0.57}, 'value')) self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [0.57, 0.91]) binary_heap.insert(HeapNode({'name': 'B', 'value': 0.34}, 'value')) self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [0.34, 0.91, 0.57])
def test_extract(self): bh = BinaryHeap(compare=lambda a, b: a < b) bh.insert(4) bh.insert(1) bh.insert(1) bh.insert(3) bh.insert(3) bh.insert(2) bh.insert(2) self.assertEqual(4, bh.extract()) self.assertEqual(3, bh.extract()) self.assertEqual(3, bh.extract()) self.assertEqual(2, bh.extract()) self.assertEqual(2, bh.extract()) self.assertEqual(1, bh.extract()) self.assertEqual(1, bh.extract())
def make_tree(freq_table): """ Constructs and returns the Huffman tree from the given frequency table. """ trees = BinaryHeap() trees.insert(TreeLeaf(None), 1) for (symbol, freq) in freq_table.items(): trees.insert(TreeLeaf(symbol), freq) while len(trees) > 1: right, rfreq = trees.popmin() left, lfreq = trees.popmin() trees.insert(TreeBranch(left, right), lfreq+rfreq) tree, _ = trees.popmin() return tree
def least_cost_path(graph, start, dest, cost): """ Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: least: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. >>> g = Graph({1, 2, 3, 4, 5}, [(1, 4), (2, 3), (4, 2)]) >>> location = {1: (1, 0), 2: (4, 0), 3: (10, 0), 4: (2, 0), 5: (10, 10)} >>> cost = CostDistance(location) >>> print(least_cost_path(g, 1, 2, cost)) [1, 4, 2] >>> print(least_cost_path(g, 1, 3, cost)) [1, 4, 2, 3] >>> print(least_cost_path(g, 1, 5, cost)) [] >>> H = Graph({1, 2, 3, 4, 5, 6}, [(1, 2), (2, 3), (2, 4), (4, 3), (3, 5), (1, 6), (6, 5)]) >>> locationtwo = {1: (0, 0), 2: (1, 1), 3: (3, 1), 4: (2, 2), 5: (0, 6), 6: (-100, 100)} >>> testcost = CostDistance(locationtwo) >>> print(least_cost_path(H, 1, 5, testcost)) [1, 2, 3, 5] """ reached = {} events = BinaryHeap() events.insert((start, start), 0) while len(events) > 0: (u, v), time = events.popmin() if v not in reached.keys(): reached[v] = u for w in graph.neighbours(v): events.insert((v, w), time + cost.distance((v, w))) ''' reached is a dictionary of all visted verices. The value of each key is represented by the previous vertice visited to get to that vertice. If we cannot find our destination in reached, then that means there is no such path in the given graph to get from start to dest, and so we would return an empty array. otherwise, we make our list least by working in reverse from our dest, all the way back to start by using the keys in reached to find the previous vertice ''' if dest not in reached: return [] least = [dest] step = dest while step != start: least.append(reached[step]) step = reached[step] least.reverse() return least
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: print("Loop 1") vertices, time = events.popmin() print(type(vertices)) print(type(vertices[0])) if vertices[1] not in reached: reached[vertices[1]] = vertices[0]# vertices[0] = reached[vertices[1]] burn vertex v, record predecessor u for n in graph.neighbours(vertices[1]): # new event: edge (v,w) started burning print("Loop 2") events.insert(([vertices[1]], n), time + cost.distance((vertices[1], n))) if vertices[1] == dest: break #return reached print("Done") #find path to see if a path exists # return empty list if dest cannot be reached/if no path from start to dest exists if dest not in reached: return [] # if the destination gets reached we can form a minimum path current = dest path = [current] while current != start: # print("Loop 3") print(dest) print(type(dest)) current = reached[current] #current = reached[current] path.append(current) #or while reached[current] != start: #path.append(reached[current]) # current = reached[current] path = path[::-1] return path
class AStar(): """ Paramaters: map: Reference to map object start_node_id: Index of start node dest_node_id: Index of destination node """ def __init__(self, map): self.map = map # Hash set to keep track of open intersections. The hash set will contain intersection IDs only # and will be used to find out if an intersection ID is already there is the set self.open = set() # Hash set to keep track of closed intersection ID. The hash set will contain intersection IDs only # and will be used to find out self.closed = set() # Dictionary to keep track of cost. The dictionary is keyed by intersection id # Cost will have three components. # g = true of cost of getting from source node to current node # h = estimated cost of getting from current node to destination node # f = total cost = g + h self.cost = {} # List used to store the actual path from destination path back to source intersection. # Once the shortest path to destination intersection is found, this will be reversed and returned as path from # source to destination self.nodes_in_path = [] # Dictionary to keep track of parent intersection. It is keyed by current intersection id. # Parent intersection ID is the intersection from where the current intersection is reached self.parent = {} # Initialize min binary heap. This will be used to get the intersection with minimum cost self.cost_heap = BinaryHeap() # Private method to get the intersection with minimum total cost def _get_node_with_min_cost(self): min_value_node = self.cost_heap.delete_min() # Remove the intersection with min cost from list of open intersections self.open.remove(min_value_node.id) # Put that intersection into closed intersections self.closed.add(min_value_node.id) return min_value_node # Returns the list of intersections that can be reached from the current intersection def _get_next_nodes(self, current_node_id): return self.map.roads[current_node_id] # Method that performs A-star search def search(self, start_node_id, dest_node_id): # Initialize the cost structure of first intersection self.cost[start_node_id] = { 'g': 0, # True cost of getting from start node to this node. This is calculated in terms of number of hops (edges) 'h': 0, # Estimated cost of getting this node to destination node 'f': 0 # Total cost. It is a computed field f = g + h } # Put the start node as the first open node where the search starts self.open.add(start_node_id) self.cost_heap.insert(HeapNode({'id': start_node_id, 'f': 0, 'g': 0, 'h' : 0}, 'f')) iteration = 1 while len(self.open) > 0: # Among all nodes which are open find the node with minimum total cost # This will be the nest node to explore current_node = self._get_node_with_min_cost() if current_node.id == dest_node_id: end_node = current_node.id while end_node is not None: self.nodes_in_path.append(end_node) end_node = self.parent.get(end_node, None) self.nodes_in_path.reverse() return self.nodes_in_path children = self._get_next_nodes(current_node.id) for child in children: # If the intersection is already in closed set then there is no need to explore it if child in self.closed: continue g = self.cost[current_node.id]['g'] + \ math.sqrt( math.pow(self.map.intersections[current_node.id][0] - self.map.intersections[child][0], 2) + \ math.pow(self.map.intersections[current_node.id][1] - self.map.intersections[child][1], 2)) # Estimate distance to destination intersection. This is pre euclidean distance. # This heuristic will always be less than or equal to the true distance of the road # connecting the two intersections h = math.sqrt( math.pow(self.map.intersections[child][0] - self.map.intersections[dest_node_id][0], 2) + \ math.pow(self.map.intersections[child][1] - self.map.intersections[dest_node_id][1], 2)) # If the child intersection is not in open set then put it in the list to explore # Compute the cost (cost to reach this intersection, estimated cost to reach destination # from here and total cost for this intersection # The child intersection may already be there if there way another intersection leading to this one # Update the cost values if cost of reaching this child intersection is lower than cost value # associated with the intersection if child not in self.open: self.cost[child] = {'g' : g, 'h' : h, 'f' : g + h} self.parent[child] = current_node.id self.open.add(child) self.cost_heap.insert(HeapNode({'id': child, 'f': g + h, 'g': g, 'h' : h}, 'f')) elif self.cost[child]['f'] > g + h: self.cost[child] = {'g' : g, 'h' : h, 'f' : g + h} self.parent[child] = current_node.id self.cost_heap.build_heap() #if iteration >= 1: # sys.exit(0) iteration += 1 # Generates SVG diagram for the graph and the shortest path found using A* search def draw_astar_map(self, path, colors, output_file): graph = pydot.Dot(graph_type='graph', rankdir="LR") nodes = {} edges = set() for k, v in self.map.intersections.items(): n = pydot.Node(k, shape="circle", style="filled", fillcolor="cyan" if k not in path else colors[0], tooltip="(%f,%f)" % (v[0], v[1])) nodes[k] = n graph.add_node(n) for src, dst in enumerate(self.map.roads): for d in dst: if (src,d) not in edges: distance = math.sqrt(math.pow(self.map.intersections[src][0] - self.map.intersections[d][0], 2) + \ math.pow(self.map.intersections[src][1] - self.map.intersections[d][1], 2)) edges.add((src,d)) edges.add((d,src)) if src in path and d in path: color = colors[1] style="bold" else: color = "black" style = "dashed" graph.add_edge(pydot.Edge(nodes[src], nodes[d], style=style, label="%0.2f" % distance, color=color, tooltip="%0.2f" % distance)) graph.write_svg(output_file)
def least_cost_path(graph, start, dest, cost): """ Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} #search tree events = BinaryHeap() #uses heap to store data on nodes and weights events.insert((start, start), 0) #starting vertex burns at time 0 edges = graph.get_edges while len(events) > 0: curr = events.popmin() u, v = curr[0][0], curr[0][1] time = curr[1] if v not in reached: reached[v] = u #burn the vertex v and add u to the record neighbors = graph.neighbours(v) for w in neighbors: newTime = time + cost.distance( (v, w) ) #stores the time taken to reach new node from previous events.insert((v, w), newTime) shortest_path = [] shortest_path.append(dest) next_node = reached[dest] while next_node != start: #iterates through the search tree from end to start, #collecting visited vertices and storing them in path array for el in reached: if el == next_node: shortest_path.append(next_node) next_node = reached[el] shortest_path.append(next_node) #removes any doubles: newList = [] for el in shortest_path: if el not in newList: newList.append(el) return list(reversed(newList))
class Ara: def __init__(self, initial_state, heuristic, weight, max_expansions): self.expansions = 0 self.generated = 0 self.initial_state = initial_state self.heuristic = heuristic self.weight = weight self.open = None self.cost = None self.max_expansions = max_expansions self.last_solution = None def estimate_suboptimality(self): cost = self.cost denominator = min(self.open, key=lambda x: x.g + x.h) denominator_num = denominator.h + denominator.g return cost / denominator_num def fvalue(self, g, h): return g + self.weight * h def adjust_weight(self): denominator = min(self.open, key=lambda x: x.g + x.h) denominator_num = denominator.h + denominator.g self.weight = self.last_solution.g / denominator_num def recalculate_open(self): for i in self.open: i.key = self.fvalue(i.g, i.h) self.open.reorder() def search(self): self.start_time = time.process_time() self.open = BinaryHeap() self.expansions = 0 initial_node = Node(self.initial_state) initial_node.g = 0 initial_node.h = self.heuristic(self.initial_state) initial_node.key = self.fvalue(0, initial_node.h) # asignamos el valor f self.open.insert(initial_node) self.generated = {} self.generated[self.initial_state] = initial_node while self.expansions <= self.max_expansions: while not self.open.is_empty(): if self.expansions >= self.max_expansions: return self.last_solution n = self.open.extract() if not self.open.is_empty() and self.open.top().key == n.key: tied_node = self.open.top() if tied_node.g < n.g: tied_node = self.open.extract() self.open.insert(n) n = tied_node if n.state.is_goal(): self.end_time = time.process_time() self.cost = n.g self.last_solution = n self.adjust_weight() self.recalculate_open() #print('aaa') yield n if not (self.last_solution is not None and n.g + n.h >= self.last_solution.g): succ = n.state.successors() self.expansions += 1 if self.expansions >= self.max_expansions: return self.last_solution for child_state, action, cost in succ: child_node = self.generated.get(child_state) is_new = child_node is None path_cost = n.g + cost if is_new or path_cost < child_node.g: if is_new: child_node = Node(child_state, n) child_node.h = self.heuristic(child_state) self.generated[child_state] = child_node child_node.action = action child_node.parent = n child_node.g = path_cost child_node.key = self.fvalue( child_node.g, child_node.h) self.open.insert(child_node) self.end_time = time.process_time() if self.open.is_empty(): #print("empty open") return self.last_solution #return None return self.last_solution
class Astar: def __init__(self, initial_state, heuristic, weight=1): self.expansions = 0 self.generated = 0 self.initial_state = initial_state self.heuristic = heuristic self.weight = weight def estimate_suboptimality(self, result): costo = result.g mini = 1000000000 for n in self.open: f = n.h + n.g if f < mini: mini = f return costo / mini print(self.open) return 0 # este método debe ser implementado en la parte 1 def fvalue(self, g, h): return ( g + self.weight * h, h ) #Esto nos permite seleccionar primero los que tienen menor h si g+h spn iguales def search(self): self.start_time = time.process_time() self.open = BinaryHeap() self.expansions = 0 initial_node = Node(self.initial_state) initial_node.g = 0 initial_node.h = self.heuristic(self.initial_state) initial_node.key = self.fvalue(0, initial_node.h) # asignamos el valor f self.open.insert(initial_node) # para cada estado alguna vez generado, generated almacena # el Node que le corresponde self.generated = {} self.generated[self.initial_state] = initial_node while not self.open.is_empty(): n = self.open.extract() # extrae n de la open if n.state.is_goal(): self.end_time = time.process_time() return n succ = n.state.successors() self.expansions += 1 for child_state, action, cost in succ: child_node = self.generated.get(child_state) is_new = child_node is None # es la primera vez que veo a child_state path_cost = n.g + cost # costo del camino encontrado hasta child_state if is_new or path_cost < child_node.g: # si vemos el estado child_state por primera vez o lo vemos por # un mejor camino, entonces lo agregamos a open if is_new: # creamos el nodo de child_state child_node = Node(child_state, n) child_node.h = self.heuristic(child_state) self.generated[child_state] = child_node child_node.action = action child_node.parent = n child_node.g = path_cost child_node.key = self.fvalue( child_node.g, child_node.h) # actualizamos el valor f de child_node self.open.insert( child_node ) # inserta child_node a la open si no esta en la open self.end_time = time.process_time( ) # en caso contrario, modifica la posicion de child_node en open return None
def naive_construct_heap(alist): new_heap = BinaryHeap() for number in alist: new_heap.insert(number) return new_heap
from binary_heap import BinaryHeap import unittest my_list = [8, 5, 4, 9, 3, 65, 1, 66, 599, 80] my_heap = BinaryHeap() for i in my_list: my_heap.insert(i) class InsertTests(unittest.TestCase): def test_insert(self): my_heap.insert(2) self.assertTrue(my_heap.check_heap) my_heap.insert(0) self.assertTrue(my_heap.check_heap) my_heap.insert(500) self.assertTrue(my_heap.check_heap) if __name__ == '__name__': unittest.main() # command to run tests: python -m unittest -v insert_test.py
class Maze: def __init__(self, width, length, height): """ Generates all maze cells and edges """ self.width = width self.length = length self.height = height self.generated = False self.graph = Graph((width, length, height)) self.edge_queue = BinaryHeap() self.edges_added = 0 self.inter_layer_edges = 0 for x in range(width): for y in range(length): for z in range(height): self.graph.add_vertex((x, y, z)) self.areas = UnionFind([cell.get_pos() for cell in self]) self.edge_choices = RandomSet(self.graph.get_all_edges()) self.queue_factor = len(self.edge_choices) // LAYER_FACTOR for _ in range(self.queue_factor): edge = self.edge_choices.pop_random() self._queue_edge(edge) self.player = self.graph.get_cell((0, 0, 0)) self.end_cell = self.graph.get_cell( (self.width - 1, self.length - 1, self.height - 1)) self.end_cell.is_end = True def __iter__(self): """ Returns an iterator to the start of the grid """ self._i = 0 self._j = 0 self._k = 0 return self def __next__(self): """ Returns the next cell in the grid """ if self._i < self.width and self._j < self.length \ and self._k < self.height: next_cell = self.graph.get_cell((self._i, self._j, self._k)) self._i += 1 if self._i == self.width: self._i = 0 self._j += 1 if self._j == self.length: self._j = 0 self._k += 1 return next_cell else: # Done iterating over cells raise StopIteration def get_layer(self, layer): return self.graph.get_layer(layer) def get_player(self): return self.player def generate(self): """ Links two random, adjacent cells from different partitions """ # Loop until we have removed a wall while not self.generated: # chooses a random item from all possible edges # and removes this choice if len(self.edge_choices) > 1: self._queue_edge(self.edge_choices.pop_random()) (p1, p2), _ = self.edge_queue.popmin() # adds an edge (removes a wall) if it is valid if self.areas.union(p1, p2): self.graph.add_edge((p1, p2)) return self.graph.get_cell(p1), self.graph.get_cell(p2) # Check if maze is completely generated if len(self.edge_queue) == 0: self.generated = True return None def _queue_edge(self, edge): """ Put an edge into a queue to be generated. We use this to limit the amount of inter-layer edges being generated by making them cost more, which adds them progressively later to the maze. Thus, they are less likely to connect disjoint partitions, and less likely to be generated at all """ if edge[0][2] != edge[1][2]: cost = self.queue_factor * self.inter_layer_edges self.inter_layer_edges += 1 else: cost = self.edges_added self.edges_added += 1 self.edge_queue.insert(edge, cost) def player_move(self, dx, dy, dz): """ checks if an edge exists between current and next cell if so, update player """ p1 = self.player.get_pos() p2 = self.player.x + dx, self.player.y + dy, self.player.z + dz c1 = self.graph.get_cell(p1) if self.graph.is_edge((p1, p2)): self.player = self.graph.get_cell(p2) c2 = self.graph.get_cell(p2) if c2.visited: c1.visited = False c2.visited = False else: c1.flip_visit() return c1, c2 else: return c1, c1 def check_layer(self, new_layer): """ Clamps layer to be valid """ if new_layer < 0: return 0 elif new_layer >= self.height: return self.height - 1 else: return new_layer
from binary_heap import BinaryHeap if __name__ == "__main__": items = [3, 4, 10, 1, 6, 5] # items = ['T', 'P', 'R', 'N', 'H', 'O', 'A', 'E', 'I', 'G'] priorityQueue = BinaryHeap(len(items)) for item in items: priorityQueue.insert(item) print "BEFORE: ", for i in range(1, len(priorityQueue.pq)): print priorityQueue.pq[i], print print "DELETED: " + str(priorityQueue.delMax()) print "AFTER: ", for i in range(1, priorityQueue.N + 1): print priorityQueue.pq[i], print print "DELETED: " + str(priorityQueue.delMax()) print "AFTER: ", for i in range(1, priorityQueue.N + 1): print priorityQueue.pq[i],
def create_heap_for_loop_insert(unsorted_list): bh = BinaryHeap() for num in unsorted_list: bh.insert(num) x = f"{'Heap list ':<15}" return x, bh