Задача о коммивояжере (NP-полная).


Это задача с очень плохим временем выполнения равным O(n!). При добавлении ещё одного элемента время выполнения растёт с ужасающей скоростью.
У нас есть задача побывать в пяти городах, при этом побывать в каждом городе только один раз. Кроме первого, из которого мы начинаем наш путь, и в который мы должны вернуться последним действием.
Программа должна спросить название городов и их координаты (широту и долготу) и вывести маршрут с кротчайшим расстоянием для объезда всех городов.
Для подсчёта расстояния между городами по их координатам можно использовать задачу 1.1.15.task.py (см. GitHub) или искать на этом сайте задачу с названием "Расстояние между точками на Земле".

import copy
from math import acos, sin, cos, radians

def distance_f(town_tuple_1, town_tuple_2):
    latitude_1 = radians(town_tuple_1[0])
    longitude_1 = radians(town_tuple_1[-1])
    latitude_2 = radians(town_tuple_2[0])
    longitude_2 = radians(town_tuple_2[-1])
    radius_middle = 6371.01                                                                                                                     # это среднее значение радиуса Земли
    distance = radius_middle * acos(sin(latitude_1) * sin(latitude_2) + cos(latitude_1) * cos(latitude_2) * cos(longitude_1 - longitude_2))     # формула расстояния между двумя точками на планете Земля 
    return int(distance)


def coordinates_f():
    while True:
        x = input("%75s" % "Введите широту для города (в формате 55.78): ")
        y = input("%75s" % "Введите долготу для города (в формате 38.44): ")
        try:
            x = float(x)
            y = float(y)
            break
        except:
            print("%74s" % "Вы ввели не цифры или запятую вместо точки.")
    return (x, y)


#########################################################################################################################################################
# ввод данных
town1 = input("%75s" % "Ввделите название первого города: ")
coordinates1 = coordinates_f()
town2 = input("%75s" % "Ввделите название второго города: ")
coordinates2 = coordinates_f()
town3 = input("%75s" % "Ввделите название третьего города: ")
coordinates3 = coordinates_f()
town4 = input("%75s" % "Ввделите название четвёртого города: ")
coordinates4 = coordinates_f()
town5 = input("%75s" % "Ввделите название пятого города: ")
coordinates5 = coordinates_f()
print()
#########################################################################################################################################################
# создадим словарь, в котором ключ - это маршрут, а значение - это расстояние маршрута
distance_dict = dict()
points = [town1, town2, town3, town4, town5]
coordinates = [coordinates1, coordinates2, coordinates3, coordinates4, coordinates5]
coordinates_towns_dict = dict()
for a, b in zip(points, coordinates):
    coordinates_towns_dict[a] = b

for a in points:
    for b in points:
        if b != a:
            distance_dict[a + "-" + b] = distance_f(coordinates_towns_dict[a], coordinates_towns_dict[b])
#########################################################################################################################################################
# следующим сложнейшим циклом мы переберём все возможные комбинации городов, подставляя в очередной вложенный цикл один и тот же список
# на первый взгляд трудно себе это представить в голове, я рисовал на бумаге решение и картина сложилась
result = dict()
points = [town1, town2, town3, town4, town5]            # эту переменную я пересоздал только для того, чтобы она была в этом блоке для наглядности
for a in points:
    if a == town1:
        for b in points:
            if b != a:
                for c in points:
                    if c != a and c != b:
                        for d in points:
                            if d != a and d != b and d != c:
                                for e in points:
                                    if e != a and e != b and e != c and e != d:
                                        for f in points:
                                            if f == town1:
                                                result[a, b, c, d, e, f] = 0            # добавляем в словарь возможные маршруты, пока расстояние запишем 0

# заполним словарь с маршрутами и реальной дистанцией
temp_dict = copy.copy(result)
temp = ""
for a in temp_dict:
    for b in a:
        if (temp + "-" + b) in distance_dict:
            result[a] += distance_dict[(temp + "-" + b)]
        temp = b
del temp_dict

# вывод на печать всех возможных маршрутов и их протяжённости
count = 1
for a, b in result.items():
    print("%74s" % f"Маршрут № {count}:", a, "%20s" % f"  -   расстояние {b}км.")
    count += 1
print()
#########################################################################################################################################################

# вывод на печать ответа
km = (min(result.values()))
finish = [i for i in result if result[i] == km]
for i in finish:
    print(f"Маршрут: {i} является кротчайшим и составляет {km} км.")