Скачиваний:
17
Добавлен:
16.04.2021
Размер:
6.52 Кб
Скачать
"""
Программа для нахождения кратчайшего пути между двумя точками в ограниченном N-мерном дискретном метрическом
подпространстве при помощи алгоритма A*
"""

import heapq
import threading
from math import inf
from random import sample
from typing import Set, List, Tuple, Callable

global_wait = False


class Heuristic:
    """
    Эвристики поиска: "Октиль", Манхэттена, Чебышёва, Евклида
    """

    @staticmethod
    def octile(first: Tuple[int], second: Tuple[int]):
        diffs = [abs(f - s) for f, s in zip(first, second)]
        return max(diffs) + (2 ** 0.5 - 1) * min(diffs)

    @staticmethod
    def manhattan(first: Tuple[int], second: Tuple[int]):
        return sum(abs(f - s) for f, s in zip(first, second))

    @staticmethod
    def chebyshev(first: Tuple[int], second: Tuple[int]):
        return max(abs(f - s) for f, s in zip(first, second))

    @staticmethod
    def euclidean(first: Tuple[int], second: Tuple[int]):
        return sum((f - s) ** 2 for f, s in zip(first, second)) ** 0.5


def a_star(space: Set[Tuple[int]], begin_point: Tuple[int], end_point: Tuple[int],
           heuristic: Callable = Heuristic.manhattan) -> List[Tuple[int]]:
    """
    Алгоритм A*. Использует приоритетную очередь.
    :param space: ограниченное N-мерное дискретное метрическое подпространство
    :param begin_point: начальная точка
    :param end_point: конечная точка
    :param heuristic: функция эвристики
    :return: кратчайший путь между начальной и конечной точками
    """
    assert begin_point not in space
    assert end_point not in space
    global global_wait
    global_wait = True
    current = []
    n_points, n_axes = (len(space), len(begin_point))
    q = []
    cost = 0
    heapq.heappush(q, (cost, end_point))
    directions = []
    for x in (-1, 1):
        for p in range(n_axes):
            temp = [0] * n_axes
            temp[p] = x
            directions.append(temp)
    parent_point = {end_point: end_point}
    cost_map = {end_point: cost}
    while q and global_wait:
        current = heapq.heappop(q)[1]
        if current == begin_point:
            break
        cost = cost_map.get(current) + 1
        for d in directions:
            neighbor = tuple(t + dt for t, dt in zip(current, d))
            if neighbor in space:
                continue
            if cost < cost_map.get(neighbor, inf):
                cost_map[neighbor] = cost
                parent_point[neighbor] = current
                heapq.heappush(q, (cost + heuristic(current, begin_point), neighbor))
    if not global_wait:
        return list()
    path = list()
    if current == begin_point:
        path.append(current)
        while current != end_point:
            current = parent_point[current]
            path.append(current)
    return path


def thread_stop():
    """
    Функция, которая вызывается после срабатывания 15-секундного таймера
    """
    global global_wait
    global_wait = False


def check(value: List[Tuple[int]]):
    """
    Функция проверки -- если таймер сработал, то пути (вероятно) не существует
    :param value: одно из проверяемых значений -- найденный путь (если пуст, то пути не существует)
    """
    if not value and global_wait:
        print("Путь между точками отсутствует")
        exit(9)
    if not value and not global_wait:
        print("15 секунд поиска прошло. Вероятно, путь между точками отсутствует.")
        exit(10)


def main():
    """
    Точка входа
    """
    points = eval("{" + input("Введите точки-ограничители (стены): ") + "}")
    if not isinstance(points, set) or not points or not all(
            isinstance(x, tuple) and all(isinstance(t, int) for t in x) for x in points):
        print("Точки-ограничители не представлены в виде множества точек с целыми координатами")
        exit(1)
    t = len(sample(points, 1)[0])
    if not all(len(x) == t for x in points):
        print("Размеры точек не совпадают")
        exit(2)
    begin = eval(input("Введите начальную точку: "))
    if not isinstance(begin, tuple) or not all(isinstance(x, int) for x in begin):
        print("Начальная точка не представлена в виде кортежа целых чисел")
        exit(3)
    if not len(begin) == t:
        print("Размеры точек не совпадают")
        exit(4)
    if begin in points:
        print("Начальная точка не может быть стеной")
        exit(5)
    end = eval(input("Введите конечную точку: "))
    if not isinstance(end, tuple) or not all(isinstance(x, int) for x in begin):
        print("Конечная точка не представлена в виде кортежа целых чисел")
        exit(6)
    if not len(end) == t:
        print("Размеры точек не совпадают")
        exit(7)
    if end in points:
        print("Конечная точка не может быть стеной")
        exit(8)
    timer = threading.Timer(15, thread_stop)
    r = [[], [], [], []]
    timer.start()
    r[0] = a_star(points, begin, end, Heuristic.octile)
    timer.cancel()
    check(r[0])
    r[1] = a_star(points, begin, end, Heuristic.manhattan)
    check(r[1])
    r[2] = a_star(points, begin, end, Heuristic.chebyshev)
    check(r[2])
    r[3] = a_star(points, begin, end, Heuristic.euclidean)
    check(r[3])
    print("Результаты поиска")
    print("Octile heuristic: ", r[0])
    print("Manhattan heuristic: ", r[1])
    print("Chebyshev heuristic: ", r[2])
    print("Euclidean heuristic: ", r[3])


if __name__ == '__main__':
    try:
        main()
    except ():
        print("\nВозникла ошибка работы программы")
    input()