diff --git a/genetic_algorithm/genetic_algorithm.py b/genetic_algorithm/genetic_algorithm.py index cbe5c56..61b1150 100644 --- a/genetic_algorithm/genetic_algorithm.py +++ b/genetic_algorithm/genetic_algorithm.py @@ -1,11 +1,15 @@ import json from datetime import datetime, timedelta import random -from typing import Dict, List +from typing import Dict, List, Tuple, Any from fastapi import FastAPI, HTTPException, Request -def load_graph_from_request(request) -> Dict[str, Dict[str, List]]: +from schemas import TripRequest + + +def load_graph_from_request(request: TripRequest) -> Tuple[Dict[str, Dict[str, List[Any]]], str, str, List[Dict]]: graph = {} + flights_data = [] # Загрузить данные о полетах из запроса flights = request.flights @@ -19,6 +23,15 @@ def load_graph_from_request(request) -> Dict[str, Dict[str, List]]: departure_time = datetime.fromisoformat(flight.departureTime) destination_time = datetime.fromisoformat(flight.destinationTime) + # Сохранение данных о полете для вывода + flights_data.append({ + "id": flight.id, + "departurePoint": departure_point, + "destinationPoint": destination_point, + "departureTime": departure_time, + "destinationTime": destination_time + }) + # Проверка наличия вершин в графе и их создание при необходимости if departure_point not in graph: graph[departure_point] = {} @@ -28,9 +41,9 @@ def load_graph_from_request(request) -> Dict[str, Dict[str, List]]: # Добавление ребра в граф graph[departure_point][destination_point] = [flight.distance, departure_time, destination_time] - return graph, start_point, end_point + return graph, start_point, end_point, flights_data + -# Функция для вычисления длины и времени пути с учетом минимального интервала времени def path_length_and_time(path, graph): length = 0 if path[0] not in graph or path[1] not in graph[path[0]]: @@ -49,8 +62,8 @@ def path_length_and_time(path, graph): end_time = graph[path[i]][path[i + 1]][2] return length, start_time, end_time -# Функция для генерации начальной популяции -def generate_population(size, start, end, graph, max_attempts=100): + +def generate_population(size, start, end, graph, max_attempts=1000): population = [] for i in range(size): attempts = 0 @@ -74,13 +87,12 @@ def generate_population(size, start, end, graph, max_attempts=100): attempts += 1 return population -# Функция для селекции родителей + def select_parents(population, graph): sorted_population = sorted(population, key=lambda p: path_length_and_time(p, graph)[0]) return sorted_population[:len(sorted_population) // 2] -# Функция для скрещивания (кроссинговера) def crossover(parent1, parent2): crossover_point = len(parent1) // 2 child = parent1[:crossover_point] @@ -90,7 +102,6 @@ def crossover(parent1, parent2): return child -# Функция для мутации def mutate(child): if len(child) <= 2: return child # Если путь состоит из одной вершины, мутация не требуется @@ -100,9 +111,18 @@ def mutate(child): return child -# Главная функция генетического алгоритма -def genetic_algorithm(start, end, graph, population_size=100, generations=100): +def update_best_paths(best_paths, path, graph, max_best_paths=3): + length, _, _ = path_length_and_time(path, graph) + if path not in best_paths: + 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, population_size=100, generations=100): population = generate_population(population_size, start, end, graph) + best_paths = {} # Словарь для хранения уникальных лучших маршрутов и их длин for generation in range(generations): parents = select_parents(population, graph) new_population = parents[:] @@ -112,8 +132,35 @@ def genetic_algorithm(start, end, graph, population_size=100, generations=100): child = crossover(parent1, parent2) if random.random() < 0.1: # Вероятность мутации child = mutate(child) - new_population.append(child) + if child[-1] == end: + new_population.append(child) + update_best_paths(best_paths, tuple(child), graph) population = new_population - best_path = min(population, key=lambda p: path_length_and_time(p, graph)[0]) - best_length, start_time, end_time = path_length_and_time(best_path, graph) - return best_path, best_length, start_time, end_time \ No newline at end of file + + best_paths = sorted(best_paths.items(), key=lambda item: item[1]) + top_paths = [list(path) for path, _ in best_paths[:3]] + + result = [] + for path in top_paths: + path_data = [] + for i in range(len(path) - 1): + for flight in flights_data: + if flight['departurePoint'] == path[i] and flight['destinationPoint'] == path[i + 1]: + path_data.append({ + "id": flight['id'], + "departurePoint": flight['departurePoint'], + "destinationPoint": flight['destinationPoint'] + }) + break + length, start_time, end_time = path_length_and_time(path, graph) + # Проверка на корректность длины пути и времени + if length == float('inf') or start_time is None or end_time is None: + continue + result.append({ + "to": path_data, + "start_time": start_time.isoformat() if start_time else None, + "end_time": end_time.isoformat() if end_time else None, + "best_length": length + }) + + return result diff --git a/router_class.py b/router_class.py index 3c715ba..1f4cb31 100644 --- a/router_class.py +++ b/router_class.py @@ -17,9 +17,11 @@ router = APIRouter( @router.post("/get_flight/") async def get_flight(request: TripRequest): - graph, start_point, end_point = load_graph_from_request(request) - best_path, best_length, start_time, end_time = genetic_algorithm(start_point, end_point, graph) - return best_path, best_length, start_time, end_time + graph, start_point, end_point, flights_data = load_graph_from_request(request) + result = genetic_algorithm(start_point, end_point, graph, flights_data) + if not result: + raise HTTPException(status_code=404, detail="No valid paths found") + return result @router.get("/negative") async def get_class_names() -> List[str]: