def recursive_visit(schedule: Dict, source: Airport, sink: Airport, arrival_time: timedelta, T: timedelta, solution: List, paths: List): # se ho raggiunto la destinazione posso salvare il percorso seguito if source == sink: # paths.append(solution[1].copy()) paths.append(list((solution[1]))) else: # per ogni volo in partenza dall'aeroporto in cui sono for flight in schedule[source]: # il costo del volo è la somma del tempo di attesa per il volo e della sua durata curr_cost = l(flight) - arrival_time + abs(a(flight) - l(flight)) # il nuovo costo totale di volo è la somma del precedente e di quello calcolato per il volo corrente total_cost = solution[0] + curr_cost # valuto tramite backtracking se il volo può essere preso if not backtracking_prune(arrival_time, l(flight), c(source), total_cost, T): # in caso positivo aggiungo al path corrente la soluzione e aggiorno il costo di volo solution[0] += curr_cost solution[1].append(flight) # valuto ricorsivamente i voli partenti dall'aeroporto appena aggiunto recursive_visit(schedule, d(flight), sink, a(flight), T, solution, paths) # pulisci i dati sulla visita al ritorno dalle chiamate solution[1].remove(flight) solution[0] -= curr_cost
def test_paths_available(): airports, schedule = initialize_schedule("../airports.txt", "../flights.txt") # airports, schedule = init_schedule_with_date("../airports.txt", "../fli.txt") start = airports[0] end = airports[1] shift = datetime.strptime("29-12-2018", "%d-%m-%Y") - datetime.strptime( "1-1-2018", "%d-%m-%Y") start_time = timedelta(hours=12, minutes=00) + shift total_time = timedelta(hours=8) paths = list_routes(schedule, start, end, start_time, total_time) list_costs = list() if len(paths) != 0: for path in paths: cost = timedelta(0) arrive_time = start_time for f in path: curr_cost = l(f) - arrive_time + a(f) - l(f) cost = curr_cost + cost arrive_time = a(f) if cost > total_time: print("Test test_paths_available failed") return list_costs.append(cost) if len(paths) == 5 and len(list_costs) == 5 and all( i <= total_time for i in list_costs): print("Test test_paths_available passed") else: print("Test test_paths_available failed") else: print("Test test_paths_available failed")
def test_path_available_check_it_is_the_shortest(): airports, schedule = initialize_schedule("../airports.txt", "../flights.txt") #airports, schedule = init_schedule_with_date("../airports.txt", "../fli.txt") start = airports[0] end = airports[1] shift = datetime.strptime( "29-12-2018", "%d-%m-%Y") - datetime.strptime( "1-1-2018","%d-%m-%Y") start_time = timedelta(hours=12, minutes=00) + shift path = find_route(schedule=schedule, start=start, dest=end, t=start_time) cost = None if len(path) != 0: cost = timedelta(0) arrive_time = start_time for f in path: curr_cost = l(f) - arrive_time + a(f) - l(f) cost = curr_cost + cost arrive_time = a(f) else: print("Test test_path_available_check_it_is_the_shortest failed") # Chiamando la list_routes() dell'esercizio 1 richiedendo come costo totale # il costo dello shortest path, la funzione deve restituire solo e soltando quel volo. paths = list_routes(schedule=schedule, source=start, dest=end, t=start_time, T=cost) if len(paths[0]) != 1: print("Test test_path_available_check_it_is_the_shortest failed") else: if str(paths[0][0]) == str(path[0]): print("Test test_path_available_check_it_is_the_shortest passed") else: print("Test test_path_available_check_it_is_the_shortest failed")
def find_route(schedule: Dict, start: Airport, dest: Airport, t: timedelta) -> List: """ La funzione trova la rotta che permette di arrivare da a a b nel minor tempo possibile, partendo ad un orario non precedente a t. (Come per l’esercizio precedente, bisogna tener conto del tempo minimo di coincidenza di ogni scalo). :param schedule: orario della compagnia :param airports: lista degli aeroporti :param start: areoporto di partenza :param dest: areoporto di arrivo :param t: orario di partenza :return: la rotta che rispetta i vincoli imposti Complessità computazionale O((n+m) log n), ma si può ragionevolmente assumere che m >> n, per cui O(m log n), in cui n è il numero di aeroporti ed m il numero di voli """ path = deque() # insieme dei voli che costituiscono il percorso più breve q = AdaptableHeapPriorityQueue() # coda a priorità per Dijkstra cloud = dict() # insieme di nodi visitati da Dijkstra con loro costo costs = dict() # dizionario dei costi ottimi valutati da Dijkstra locators = dict() # dizionario dei locators per la coda q # fase di inizializzazione dei costi per ogni aeroporto nella lista # n inserimenti => O(n log n) for airport in schedule.keys(): if airport == start: costs[airport] = timedelta(0) else: costs[airport] = timedelta(hours=1000) # sintassi per +∞ # nella coda mantengo il riferimento ad aeroporto sorgente, aeroporto destinazione e tempo di arrivo # salvo il locator per futuri aggiornamenti # ogni inserimento nella coda richiede O(log n) locators[airport] = q.add(costs[airport], (airport, None, t)) # la valutazione della coda vuota prende tempo 0(1) # le operazioni nel ciclo vengono effettuate per tutti gli aeroporti nella coda, per cui: # O(n log n) per la remove_min # O(∑z log n) = O(m log n) per le operazioni contenute nel for while not q.is_empty(): # prendi l'elemento a costo minore nella coda (aeroporto che si raggiunge con tempo minore) # ogni remove_min richiede O(log n) cost, (source, flight_taken, t_temp) = q.remove_min() # salva l'ultimo volo preso per raggiungere questo aeroporto cloud[source] = flight_taken # se ho raggiunto la destinazione, Dijkstra mi assicura che questo è il percorso più breve # esco dal ciclo if source == dest: break # per ogni volo che parte da source (aeroporto corrente) # al più z, se z è il numero di voli che partono da source for flight in schedule[source]: # valuto se l'aeroporto di destinazione del volo è stato già aggiunto alla soluzione if d(flight) not in cloud: # valuto se il volo rientra nei vincoli temporali if t_temp + c(source) <= l(flight): # se il costo per arrivare in d(f) è minore di quello noto fino ad ora, aggiornalo if costs[source] + l(flight) - t_temp + a(flight) - l( flight) < costs[d(flight)]: costs[d(flight)] = costs[source] + l( flight) - t_temp + a(flight) - l(flight) # nella coda mantieni in corrispondenza di questo aeroporto (key) il volo per # raggiungerlo e l'ora in cui ci arrivi, che è il relativo tempo di arrivo # la modifica della key di un elemento richiede tempo O(log n) q.update(locators[d(flight)], costs[d(flight)], (d(flight), flight, a(flight))) # ripercorro all'indietro i voli che mi portano a destinazione per costruire la rotta della soluzione # si utilizza una deque (double-ended queue) che è rappresentata internamente come una lista doppiamente linkata, # in modo da avere l'inserimento in testa più efficiente possibile e cioè O(1) # nel caso peggiore potrebbero essere inseriti m elementi, con complessità totale O(m) flight_to_take = cloud[dest] while flight_to_take is not None: path.appendleft(flight_to_take) flight_to_take = cloud[s(flight_to_take)] # cast a lista richiede O(n) con n numero di elementi nella collezione return list(path)