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
Example #2
0
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)