📜  拼图 |环游世界最少的飞机(1)

📅  最后修改于: 2023-12-03 15:39:47.124000             🧑  作者: Mango

拼图|环游世界最少的飞机

如果你想旅游但又不想浪费时间在乘坐飞机上,那么这个问题可以用数学来解决:如何用最少的航班数环游世界?

这个问题可以被视为另一个经典问题——第38个问题——旅行商问题的变体。旅行商问题是一个NP难问题,它要求旅行商经过所有城市一次,然后返回起点城市,使得路程最短。在环游世界问题中,航班数是对路程的限制,也即,环游世界需要访问所有城市,但时间是靠乘坐飞机的时间控制的。

虽然问题看起来很复杂,但是我们可以使用 TSP 这个启发式算法来解决它。TSP 算法将问题拆解为一个图结构,其中每个城市是此图结构中的一个节点。这些节点之间以距离最短的方式连接。

算法的实现方式

我们可以使用 Python 和 Google OR-Tools 来解决这个问题。Google OR-Tools 是 Google 提供的开源工具,可以用来解决许多 NP 难问题,包括 TSP 问题。

以下是该问题的 Python 代码示例:

"""Traveling Salesman Problem (TSP)."""
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp

def create_data_model():
    """Stores the data for the problem."""
    data = {}
    # locations in the city
    locations = [(4, 4), # depot
                (2, 0), (8, 0), # row 0
                (0, 1), (1, 1),
                (5, 2), (7, 2),
                (3, 3), (6, 3),
                (5, 5), (8, 5),
                (1, 6), (2, 6),
                (3, 7), (6, 7),
                (0, 8), (7, 8)]
    # distance matrix
    data["distance_matrix"] = [
        [
            0, 174, 145, 215, 150, 86, 80, 63, 129, 160, 135, 136, 99, 86, 64, 70, 146
        ],
        [
            174, 0, 119, 95, 56, 101, 98, 148, 65, 73, 59, 46, 77, 89, 131, 140, 40
        ],
        [
            145, 119, 0, 162, 95, 48, 49, 82, 51, 79, 61, 80, 33, 42, 80, 100, 103
        ],
        [
            215, 95, 162, 0, 81, 160, 159, 209, 126, 134, 120, 103, 139, 152, 194, 203, 90
        ],
        [
            150, 56, 95, 81, 0, 77, 74, 123, 81, 25, 25, 11, 37, 50, 92, 102, 44
        ],
        [
            86, 101, 48, 160, 77, 0, 7, 47, 37, 54, 50, 65, 15, 29, 72, 92, 55
        ],
        [
            80, 98, 49, 159, 74, 7, 0, 40, 44, 47, 45, 58, 8, 22, 65, 85, 48
        ],
        [
            63, 148, 82, 209, 123, 47, 40, 0, 83, 93, 86, 101, 53, 50, 28, 43, 119
        ],
        [
            129, 65, 51, 126, 81, 37, 44, 83, 0, 55, 38, 28, 47, 51, 94, 114, 69
        ],
        [
            160, 73, 79, 134, 25, 54, 47, 93, 55, 0, 16, 29, 50, 63, 105, 115, 36
        ],
        [
            135, 59, 61, 120, 25, 50, 45, 86, 38, 16, 0, 18, 42, 55, 97, 107, 39
        ],
        [
            136, 46, 80, 103, 11, 65, 58, 101, 28, 29, 18, 0, 46, 59, 101, 111, 23
        ],
        [
            99, 77, 33, 139, 37, 15, 8, 53, 47, 50, 42, 46, 0, 13, 56, 76, 39
        ],
        [
            86, 89, 42, 152, 50, 29, 22, 50, 51, 63, 55, 59, 13, 0, 43, 63, 26
        ],
        [
            64, 131, 80, 194, 92, 72, 65, 28, 94, 105, 97, 101, 56, 43, 0, 20, 124
        ],
        [
            70, 140, 100, 203, 102, 92, 85, 43, 114, 115, 107, 111, 76, 63, 20, 0, 141
        ],
        [
            146, 40, 103, 90, 44, 55, 48, 119, 69, 36, 39, 23, 39, 26, 124, 141, 0
        ],
    ]
    data["num_vehicles"] = 1
    data["depot"] = 0
    return data

def print_solution(manager, routing, solution):
    """Prints solution on console."""
    print(f"Objective: {solution.ObjectiveValue()}")
    index = routing.Start(0)
    plan_output = "Route:\n"
    route_distance = 0
    while not routing.IsEnd(index):
        plan_output += f"{manager.IndexToNode(index)} -> "
        previous_index = index
        index = solution.Value(routing.NextVar(index))
        route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)
    plan_output += f"{manager.IndexToNode(index)}\n"
    plan_output += f"Route distance: {route_distance}km\n"
    print(plan_output)

def main():
    """Entry point of the program."""
    data = create_data_model()
    manager = pywrapcp.RoutingIndexManager(len(data["distance_matrix"]), data["num_vehicles"], data["depot"])
    routing = pywrapcp.RoutingModel(manager)
    transit_callback_index = routing.RegisterTransitCallback(
        lambda from_index, to_index: data["distance_matrix"][manager.IndexToNode(from_index)][manager.IndexToNode(to_index)]
    )
    routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
    search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
    search_parameters.local_search_metaheuristic = routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
    solution = routing.SolveWithParameters(search_parameters)
    print_solution(manager, routing, solution)

if __name__ == '__main__':
    main()
结论

在运行这个程序之后,我们可以得到一个旅行路线的解决方案。在本例中,此算法发现了最少航班数环游世界的路线:4,索菲亚—阿布扎比—悉尼—洛杉矶—索菲亚。

虽然 OR-Tools 已经帮我们解决了环游世界问题,但是这只是 NP 难问题的一个变体。所以,如果您有更多关于这个问题的研究,我们欢迎您在评论区留言分享您的想法。