class Vertex(object): def __init__(self, location): self.edges = HashTable() self.value = location # add edge to hash table: O(n) def add_edge(self, edge): self.edges.insert(edge.identifier, edge) # find edge from id: O(n) def find_edge(self, edge_id): return self.edges.find(edge_id) # find distance to a neighboring vertex: O(n) def distance_to(self, location): return self.edges.find(location.identifier).weight
def form_parametrize_delete(): """Forming parameters for testing function delete""" dict_1 = {item: item ** 2 for item in range(100)} dict_2 = HashTable(100) result = [] for i in range(100): dict_1.pop(i) dict_2.delete(str(i)) result.append((dict_2.find(str(i)), bool(dict_1.get(i)))) return result
class TestHashTable(unittest.TestCase): def setUp(self): self.h_table = HashTable(17, 3) self.h_table.put("AA") self.h_table.put("Bc") self.h_table.put("dE") self.h_table.put("eD") def test_hash_fun(self): """Проверка работы хэш-функции""" self.assertEqual(self.h_table.hash_fun("dE"), 16) self.assertEqual(self.h_table.hash_fun("eD"), 16) def test_put(self): """Проверка записи значения по хэш-функции""" self.assertEqual(self.h_table.slots, [ None, None, 'eD', None, None, None, None, None, None, None, None, 'AA', 'Bc', None, None, None, 'dE' ]) def test_seek_slot(self): """ Проверка поиска пустого слота:""" self.h_table = HashTable(4, 3) self.h_table.put("AA") self.h_table.put("Bc") self.h_table.put("dE") self.h_table.put("eD") self.assertEqual(self.h_table.seek_slot("DD"), None) def test_find(self): """ Проверка поиска элемента по значению:""" self.h_table = HashTable(4, 3) self.h_table.put("AA") self.h_table.put("Bc") self.h_table.put("dE") self.h_table.put("eD") self.assertEqual(self.h_table.seek_slot("DD"), None) self.assertEqual(self.h_table.find("dE"), 0) self.assertEqual(self.h_table.find("eD"), 3) self.assertEqual(self.h_table.find("DD"), None)
def hash_table_test(): # Create hash table h = HashTable() # Populate it for _ in range(100): h.add(random_element()) h.add(Element('topher', 1337)) # Print the table h.print() # Try a find print(h.find('topher').output())
class Graph(object): def __init__(self): self.vertices = HashTable(20) # creates a vertex from a location and adds to vertex hash table: O(n) def add_vertex(self, location): self.vertices.insert(location.identifier, Vertex(location)) # creates a bi-directional weighted edge between two vertexes in the graph: O(n) def add_weighted_edge(self, origin, destination, weight): self.vertices.find(origin.identifier).add_edge( Edge(destination, weight)) self.vertices.find(destination.identifier).add_edge( Edge(origin, weight)) # finds the vertex matching the location: O(n) def find_vertex(self, location): return self.vertices.find(location.identifier) # finds the distance between two vertexes: O(n) def find_distance_between(self, origin, target): return self.vertices.find(origin.identifier).distance_to(target) # finds the distance between a location and where the package needs to be delivered # similar to the method above but it used to sort the priority lists def distance_to_deliver(self, location): def distance_to(package): return self.vertices.find(location.identifier).distance_to( package.destination) return distance_to # creates a closure/lambda that is used to find the distances between locations # this is used when finding the next closest location the truck should drive to def distance_from(self, origin): def distance_to(destination): return self.vertices.find( origin.identifier).distance_to(destination) return distance_to
def run(): graph = Graph() locations_hash = HashTable(20) packages_hash = HashTable(40) # loading all the location data from the csv file # populating a hash table and graph with the location data with open('location_data.csv') as csvfile: location_data = csv.reader(csvfile) # looping through location data: O(n) for data_row in location_data: location = Location(*data_row) # inserting location data into hash table: O(n) locations_hash.insert(location.identifier, location) locations_hash.insert(location.address, location) # creating graph vertexes from the location data: O(n) graph.add_vertex(location) all_packages = [] high_priority = [] low_priority = [] # this section loops through all the package data and creates three lists: high and low priority, and a list of all packages with open('package_data.csv') as csvfile: package_data = csv.reader(csvfile) for data_row in package_data: package = Package(*(data_row+[locations_hash.find(data_row[1])])) all_packages.append(package) packages_hash.insert(package.identifier, package) # packages are divided into high or low priority lists depending on whether they have approaching deadlines or special instructions # append operations are O(1) if package.is_high_priority(): high_priority.append(package) else: low_priority.append(package) # this loops through all the distance data between locations # This data is used to create edges between the vertexes in the graph with open('distance_data.csv') as csvfile: distance_data = csv.reader(csvfile) # Looping through each cell in the csv file: O(n^2) for i, data_row in enumerate(distance_data): for j, data in enumerate(data_row): if data != '': # adding a weighted edge to the graph: O(n) graph.add_weighted_edge(locations_hash.find(i), locations_hash.find(j), float(data)) start_time = timedelta(hours=8) start_location = locations_hash.find(0) # only use two trucks. First truck will make two trips. trucks = [ Truck(1, start_time, start_location), Truck(2, start_time, start_location) ] # list of times when trucks should wait to leave the station in order to optimize package distribution times_to_leave_hub = [ timedelta(hours=8), timedelta(hours=9, minutes=5), timedelta(hours=10, minutes=20) ] # sort high and low priority lists based on their distance from the main hub: O(n*log(n)) high_priority = sorted(high_priority, key=graph.distance_to_deliver(start_location)) low_priority = sorted(low_priority, key=graph.distance_to_deliver(start_location)) count = 0 truck_idx = 0 i = 0 # continous loop until all packages have been delivered. Three loops in total while count < len(all_packages): truck = trucks[truck_idx] if i < len(times_to_leave_hub): leave_hub_at = times_to_leave_hub[i] truck.wait_at_hub(leave_hub_at) # filter priority lists based on which packages the given truck can deliver: O(n) filtered_high = [p for p in high_priority if truck.can_deliver(p)] # take as many high priorty packages as the truck can fit first for package in filtered_high: # appending a package to the truck list: O(1) truck.add_package(package) count += 1 if truck.is_full(): break # if the truck isn't full yet, fill it up with nearby low priority packages if truck.is_full() is not True: filtered_low = [p for p in low_priority if truck.can_deliver(p)] for package in filtered_low: truck.add_package(package) count += 1 if truck.is_full(): break # truck delivers packages using a greedy algorithm to find the most optimized path through the graph # Time complexity: O(n^2*log(n)) truck.deliver_packages(graph, (len(all_packages) - count) > truck.max) i += 1 truck_idx = i % len(trucks) def total_distance(truck): return truck.total_distance return [sum(map(total_distance, trucks)), packages_hash, all_packages]
class Season: """A season, contains multiple tournaments and retains a scoreboard for both men and women player tracks. Attributes: circuit: The tournament circuit this season uses. previous: The previous season in this circuit. name: The name of the season. men_stats: Maps all male player names to their season statistics. women_stats: Maps all female player names to their season statistics. men_scoreboard: An array of male statistics for this season sorted by points. women_scoreboard: An array of female statistics for this season sorted by points. """ def __init__(self, circuit, previous, name: str, complete: bool, men_stats, women_stats, men_scoreboard, women_scoreboard): self.circuit = circuit self.previous = previous self.name = name self.complete = complete self.men_stats = men_stats self.women_stats = women_stats self.men_scoreboard = men_scoreboard self.women_scoreboard = women_scoreboard self.tournaments = HashTable() # <tournament name, tournament> def run(self, tournament_name): """Runs the season, given a tournament name. :param tournament_name: The tournament to run. """ tournament: Tournament = self.tournaments.find(tournament_name) if tournament is None: print('Starting a new tournament') # Check tournament type is valid. tournament_type = self.circuit.tournament_types.find( tournament_name) if tournament_type is None: print('A tournament by the name %s does not exist' % tournament_name) return # Create the tournament. previous_tournament = None if self.previous is not None: previous_tournament = self.previous.tournaments.find( tournament_name) tournament = Tournament(self, tournament_type, previous_tournament, False) tournament.men_track = self.create_track(tournament, 'men') tournament.women_track = self.create_track(tournament, 'women') self.tournaments.insert(tournament_name, tournament) else: print('Continuing tournament from saved progress') tournament.run() if len(self.tournaments) == len(self.circuit.tournament_types): self.complete = True print('Season %s has successfully complete!' % self.name) self.print_scoreboard('men') self.print_scoreboard('women') def create_track(self, tournament, gender): """Creates a new track for a given tournament and gender. Starts off with an empty scoreboard and mappings for the player stats. :param tournament: The tournament to create the track for. :param gender: The gender of the track to create. :return: The newly created track. """ stats = HashTable() for player_name, player_profile in self.circuit.get_players(gender): season_stats: SeasonStats = self.get_stats(gender).find( player_name) tournament_stats = TournamentStats(player_profile, season_stats) season_stats.tournament_stats.insert(tournament.type.name, tournament_stats) stats.insert(player_name, tournament_stats) winning_score = get_winning_score(gender) forfeit_score = get_forfeit_score(gender) previous_stats = None previous_season_scoreboard = None if tournament.previous is not None: previous_stats = tournament.previous.get_track(gender).stats previous_season_scoreboard = tournament.previous.season.get_scoreboard( gender) track_round = 1 remaining = stats.clone() scoreboard = List() return Track(gender, track_round, stats, remaining, winning_score, forfeit_score, scoreboard, previous_stats, previous_season_scoreboard) def get_stats(self, gender): """Gets the season stat mappings for a given gender. :param gender: The track gender. :return: The stat mappings. """ return self.men_stats if gender == 'men' else self.women_stats def get_scoreboard(self, gender): return self.men_scoreboard if gender == 'men' else self.women_scoreboard def set_scoreboard(self, gender, scoreboard): """Updates the scoreboard for a given gender in the season. :param gender: The gender of the track scoreboard to update. :param scoreboard: The new scoreboard to update the current with. """ if gender == 'men': self.men_scoreboard = scoreboard else: self.women_scoreboard = scoreboard def print_scoreboard(self, gender): """Prints the scoreboard for the season, given the gender of the scoreboard track to print. :param gender: The gender of the track to use in printing the scoreboard. """ print('Scoreboard for track %s in season %s' % (gender, self.name)) rank = 1 scoreboard = self.get_scoreboard(gender) for points, stats in scoreboard: print('#%d. %s at %.2f points' % (rank, stats.player.name, stats.points)) rank += 1