VRP 容量限制 (CVRP)

問題描述

你經營一個配送中心,有 5 輛貨車,每輛最多載重 2000 公斤。今天有 20 個客戶需要送貨,每個客戶的貨物重量不同。如何規劃路線,讓所有車輛的總行駛距離最短?

from ortools.constraint_solver import routing_enums_pb2, pywrapcp
import numpy as np

def create_data_model():
    """隨機產生客戶資料"""
    np.random.seed(42)
    num_customers = 20
    
    # 隨機產生客戶位置(座標)
    locations = np.random.rand(num_customers + 1, 2) * 100
    
    # 距離矩陣
    dist_matrix = []
    for i in range(num_customers + 1):
        row = []
        for j in range(num_customers + 1):
            dx = locations[i][0] - locations[j][0]
            dy = locations[i][1] - locations[j][1]
            row.append(int(np.sqrt(dx*dx + dy*dy)))
        dist_matrix.append(row)
    
    # 每個客戶的需求量(隨機 100-500 公斤)
    demands = [0] + [np.random.randint(100, 500) for _ in range(num_customers)]
    
    data = {
        "distance_matrix": dist_matrix,
        "demands": demands,
        "vehicle_capacities": [2000] * 5,  # 5 輛車,每輛載重 2000
        "num_vehicles": 5,
        "depot": 0,
    }
    return data

def print_solution(data, manager, routing, solution):
    """輸出結果"""
    print(f"\n=== CVRP 求解結果 ===")
    total_distance = 0
    total_load = 0
    
    for vehicle_id in range(data["num_vehicles"]):
        index = routing.Start(vehicle_id)
        plan_output = f"車輛 {vehicle_id} 路線:\n"
        route_distance = 0
        route_load = 0
        
        while not routing.IsEnd(index):
            node_index = manager.IndexToNode(index)
            route_load += data["demands"][node_index]
            plan_output += f" {node_index} (載重:{route_load}) →"
            previous_index = index
            index = solution.Value(routing.NextVar(index))
            route_distance += routing.GetArcCostForVehicle(
                previous_index, index, vehicle_id
            )
        
        node_index = manager.IndexToNode(index)
        plan_output += f" {node_index}\n"
        plan_output += f"  總距離: {route_distance}, 總載重: {route_load}/{data['vehicle_capacities'][vehicle_id]}\n"
        print(plan_output)
        total_distance += route_distance
        total_load += route_load
    
    print(f"所有車輛總距離: {total_distance}")
    print(f"所有車輛總載重: {total_load}")

def solve_cvrp():
    data = create_data_model()
    
    manager = pywrapcp.RoutingIndexManager(
        len(data["distance_matrix"]), data["num_vehicles"], data["depot"]
    )
    routing = pywrapcp.RoutingModel(manager)
    
    def distance_callback(from_index, to_index):
        from_node = manager.IndexToNode(from_index)
        to_node = manager.IndexToNode(to_index)
        return data["distance_matrix"][from_node][to_node]
    
    transit_callback_index = routing.RegisterTransitCallback(distance_callback)
    routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
    
    # 加入容量限制
    def demand_callback(from_index):
        from_node = manager.IndexToNode(from_index)
        return data["demands"][from_node]
    
    demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)
    routing.AddDimensionWithVehicleCapacity(
        demand_callback_index, 0, data["vehicle_capacities"], True, "Capacity"
    )
    
    search_parameters = pywrapcp.DefaultRoutingSearchParameters()
    search_parameters.first_solution_strategy = (
        routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
    )
    search_parameters.local_search_metaheuristic = (
        routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
    )
    search_parameters.time_limit.seconds = 10
    
    solution = routing.SolveWithParameters(search_parameters)
    
    if solution:
        print_solution(data, manager, routing, solution)
    else:
        print("找不到解!")

if __name__ == "__main__":
    solve_cvrp()

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!