diff --git a/genetic_algorithm/genetic_algorithm.py b/genetic_algorithm/genetic_algorithm.py index 385e1ff..0c2e2ef 100644 --- a/genetic_algorithm/genetic_algorithm.py +++ b/genetic_algorithm/genetic_algorithm.py @@ -1,15 +1,12 @@ import json -from datetime import datetime, timedelta, date +from datetime import datetime, timedelta import random from typing import Dict, List, Tuple, Any from fastapi import FastAPI, HTTPException, Request from schemas import TripRequest - -def load_graph_from_request(request: TripRequest) -> tuple[ - dict[str, dict[Any, Any]], dict[str, dict[Any, Any]], str, str, list[dict[str, datetime | str | int]], list[ - dict[str, datetime | str | int]], date, date | None]: +def load_graph_from_request(request: TripRequest) -> Tuple[Dict[Tuple[str, str], List[Any]], Dict[Tuple[str, str], List[Any]], str, str, List[Dict], List[Dict], datetime, datetime]: graphTo = {} flightsDataTo = [] graphBack = {} @@ -41,14 +38,12 @@ def load_graph_from_request(request: TripRequest) -> tuple[ "destinationTime": destination_time }) - # Проверка наличия вершин в графе и их создание при необходимости - if departure_point not in graphTo: - graphTo[departure_point] = {} - if destination_point not in graphTo: - graphTo[destination_point] = {} + # Проверка наличия ребра в графе и его создание при необходимости + if (departure_point, destination_point) not in graphTo: + graphTo[(departure_point, destination_point)] = [] # Добавление ребра в граф - graphTo[departure_point][destination_point] = [flight.distance, departure_time, destination_time] + graphTo[(departure_point, destination_point)].append([flight.distance, departure_time, destination_time]) # Добавить каждый полет в граф for flight in flightsBack: @@ -66,65 +61,54 @@ def load_graph_from_request(request: TripRequest) -> tuple[ "destinationTime": destination_time }) - # Проверка наличия вершин в графе и их создание при необходимости - if departure_point not in graphBack: - graphBack[departure_point] = {} - if destination_point not in graphBack: - graphBack[destination_point] = {} + # Проверка наличия ребра в графе и его создание при необходимости + if (departure_point, destination_point) not in graphBack: + graphBack[(departure_point, destination_point)] = [] # Добавление ребра в граф - graphBack[departure_point][destination_point] = [flight.distance, departure_time, destination_time] + graphBack[(departure_point, destination_point)].append([flight.distance, departure_time, destination_time]) - print(graphTo) - return graphTo,graphBack, start_point, end_point, flightsDataTo, flightsDataBack, departure_date, departure_date_return + return graphTo, graphBack, start_point, end_point, flightsDataTo, flightsDataBack, departure_date, departure_date_return -def path_length_and_time(path, graph, departure_date): +def path_length_and_time(path, graph): length = 0 - if path[0] not in graph or path[1] not in graph[path[0]]: + if (path[0], path[1]) not in graph: return float('inf'), None, None - start_time = graph[path[0]][path[1]][1] + start_time = graph[(path[0], path[1])][0][1] end_time = start_time for i in range(len(path) - 1): - if path[i] not in graph or path[i + 1] not in graph[path[i]]: + if (path[i], path[i + 1]) not in graph: return float('inf'), start_time, end_time - - if departure_date != start_time.date(): - return float('inf'), start_time, end_time - - if i > 0: - print("Start time:", start_time) - print("graph date:", graph[path[i]][path[i + 1]][1]) - if start_time + timedelta(minutes=180) > graph[path[i]][path[i + 1]][1]: - print("Departure date and start time date do not match") - return float('inf'), start_time, end_time - - length += graph[path[i]][path[i + 1]][0] - end_time = graph[path[i]][path[i + 1]][2] - start_time = end_time - + flight = graph[(path[i], path[i + 1])][0] + length += flight[0] + if i > 0: # Пропуск первой вершины + if end_time + timedelta(minutes=180) > flight[1]: + return float('inf'), start_time, end_time # Неприемлемый путь + end_time = flight[2] return length, start_time, end_time def generate_population(size, start, end, graph, max_attempts=100): population = [] + nodes = list(set([k[0] for k in graph.keys()] + [k[1] for k in graph.keys()])) for i in range(size): attempts = 0 while attempts < max_attempts: path = [start] visited = set(path) while path[-1] != end: - if path[-1] not in graph or not graph[path[-1]]: + next_nodes = [k[1] for k in graph.keys() if k[0] == path[-1]] + if not next_nodes: break - next_node = random.choice(list(graph[path[-1]].keys())) + next_node = random.choice(next_nodes) if next_node in visited: break path.append(next_node) visited.add(next_node) - # Если длина пути превышает количество узлов в графе, выйти из цикла - if len(path) > len(graph): + if len(path) > len(nodes): break if path[-1] == end: population.append(path) @@ -132,9 +116,10 @@ def generate_population(size, start, end, graph, max_attempts=100): attempts += 1 return population -def select_parents(population, graph, departure_date): - sorted_population = sorted(population, key=lambda p: path_length_and_time(p, graph, departure_date)[0]) - valid_parents = [p for p in sorted_population if path_length_and_time(p, graph, departure_date)[0] != float('inf')] + +def select_parents(population, graph): + sorted_population = sorted(population, key=lambda p: path_length_and_time(p, graph)[0]) + valid_parents = [p for p in sorted_population if path_length_and_time(p, graph)[0] != float('inf')] return valid_parents[:len(valid_parents) // 2] @@ -156,19 +141,20 @@ def mutate(child): return child -def update_best_paths(best_paths, path, max_best_paths=10): +def update_best_paths(best_paths, path, graph, max_best_paths=10): + length, _, _ = path_length_and_time(path, graph) if path not in best_paths: - best_paths[path] = None + best_paths[path] = length if len(best_paths) > max_best_paths: worst_path = max(best_paths, key=best_paths.get) del best_paths[worst_path] -def genetic_algorithm(start, end, graph, flights_data, type, departure_date, population_size=5, generations=5): +def genetic_algorithm(start, end, graph, flights_data, type, departure_date, population_size=2000, generations=100): population = generate_population(population_size, start, end, graph) best_paths = {} # Словарь для хранения уникальных лучших маршрутов и их длин for generation in range(generations): - parents = select_parents(population, graph, departure_date) + parents = select_parents(population, graph) # Если не удалось выбрать родителей, завершить алгоритм if not parents: @@ -183,7 +169,7 @@ def genetic_algorithm(start, end, graph, flights_data, type, departure_date, pop child = mutate(child) if child[-1] == end: new_population.append(child) - update_best_paths(best_paths, tuple(child)) + update_best_paths(best_paths, tuple(child), graph) population = new_population best_paths = sorted(best_paths.items(), key=lambda item: item[1]) @@ -205,4 +191,4 @@ def genetic_algorithm(start, end, graph, flights_data, type, departure_date, pop type: path_data, }) - return result \ No newline at end of file + return result