运筹学练习Python精解——指派问题
练习1
有一份中文说明书,需译成英、日、德、俄四种文字,分别记作A、B、C、D。现有甲、乙、丙、丁四人,他们将中文说明书译成不同语种的说明书所需时间如下表所示,问如何分派任务,可使总时间最少?
任务 | A | B | C | D |
---|---|---|---|---|
甲 | 6 | 7 | 11 | 2 |
乙 | 4 | 5 | 9 | 8 |
丙 | 3 | 1 | 10 | 4 |
丁 | 5 | 9 | 8 | 2 |
该问题是典型的指派问题。假设有n个人和 n 项任务,每个人完成每项任务的时间可以表示为一个 n×n的矩阵 C,其中cij表示第i个人完成第j项任务所需的时间。我们的目标是找到一个指派方案,使得所有任务完成的总时间最小。
import numpy as np
from scipy.optimize import linear_sum_assignment
# 定义成本矩阵
cost_matrix = np.array([
[6, 7, 11, 2],
[4, 5, 9, 8],
[3, 1, 10, 4],
[5, 9, 8, 2]
])
# 使用匈牙利算法求解指派问题
row_ind, col_ind = linear_sum_assignment(cost_matrix)
# 输出结果
print("最优指派方案:")
for i in range(len(row_ind)):
print(f"任务 {chr(65 + col_ind[i])} 分配给 人员 {chr(65 + row_ind[i])}, 所需时间: {cost_matrix[row_ind[i], col_ind[i]]}")
print(f"总最小时间: {cost_matrix[row_ind, col_ind].sum()}")
优指派方案:
任务 D 分配给 人员 A, 所需时间: 2
任务 A 分配给 人员 B, 所需时间: 4
任务 B 分配给 人员 C, 所需时间: 1
任务 C 分配给 人员 D, 所需时间: 8
总最小时间: 15
练习2
把甲、乙、丙三种原料分别交给三家工厂制成三种不同的制品A、B、C。同样的制品因为原料不同,利润也不同。这三种原料制成的三种制品的利润如下表所示:
制品 | 原料甲 | 原料乙 | 原料丙 |
---|---|---|---|
A | 7 | 9 | 14 |
B | 14 | 16 | 11 |
C | 8 | 12 | 10 |
问:用哪一种原料制成哪一种制品总利润最大?
我们将利润矩阵表示为一个3×3的矩阵P,其中 pij 表示使用第 i 种原料制造第j种制品的利润。利润矩阵如下:
import numpy as np
from scipy.optimize import linear_sum_assignment
# 定义利润矩阵
profit_matrix = np.array([
[7, 14, 8],
[9, 16, 12],
[14, 11, 10]
])
# 将利润矩阵转换为成本矩阵
max_profit = np.max(profit_matrix)
cost_matrix = max_profit - profit_matrix
# 使用匈牙利算法求解指派问题
row_ind, col_ind = linear_sum_assignment(cost_matrix)
# 原料名字
raw_materials = ['甲', '乙', '丙']
products = ['A', 'B', 'C']
# 输出结果
print("最优指派方案:")
total_profit = 0
for i in range(len(row_ind)):
print(f"原料 {raw_materials[row_ind[i]]} 分配给 制品 {products[col_ind[i]]}, 所得利润: {profit_matrix[row_ind[i], col_ind[i]]}")
total_profit += profit_matrix[row_ind[i], col_ind[i]]
print(f"总最大利润: {total_profit}")
最优指派方案:
原料 甲 分配给 制品 B, 所得利润: 14
原料 乙 分配给 制品 C, 所得利润: 12
原料 丙 分配给 制品 A, 所得利润: 14
总最大利润: 40
练习3
在遥远的地方有一位酋长,他想把三个女儿嫁出去。假定三个女儿为A、B、C,三位求婚者为X、Y、Z。每位求婚者对A、B、C愿出的财礼数视其对她们的喜欢程度而定:
A | B | C | |
---|---|---|---|
X | 3 | 5 | 26 |
Y | 27 | 10 | 28 |
Z | 1 | 4 | 7 |
问酋长应如何嫁女,才能获得最多的财礼(从总体上讲,他的女婿最喜欢他的女儿)。
import numpy as np
from scipy.optimize import linear_sum_assignment
# The given gift matrix
gift_matrix = np.array([
[3, 5, 26],
[27, 10, 28],
[1, 4, 7]
])
# Since linear_sum_assignment solves for minimum cost, we convert the problem to a minimization problem
# by subtracting the values from a large number (max value in the matrix + 1).
cost_matrix = gift_matrix.max() + 1 - gift_matrix
# Applying the Hungarian algorithm to find the optimal assignment
row_ind, col_ind = linear_sum_assignment(cost_matrix)
# Extracting the optimal assignment and the maximum gift value
optimal_assignment = list(zip(row_ind, col_ind))
max_gift_value = gift_matrix[row_ind, col_ind].sum()
# Mapping indices to names
daughters = ['A', 'B', 'C']
suitors = ['X', 'Y', 'Z']
# Creating a readable output
assignment_output = []
for row, col in optimal_assignment:
assignment_output.append(f"Daughter {daughters[row]} marries Suitor {suitors[col]}")
print("Optimal assignment:")
for assignment in assignment_output:
print(assignment)
print("Maximum gift value:", max_gift_value)
Optimal assignment:
Daughter A marries Suitor Z
Daughter B marries Suitor X
Daughter C marries Suitor Y
Maximum gift value: 57
练习4
分配甲、乙、丙、丁4个人去完成A、B、C、D、E 5项任务,每个人完成各项任务的时间见下表。由于任务数多于人数,故考虑:
(1) 任务E必须完成,其他4项中可任选3项完成;
(2) 其中有一人完成两项,其他每人完成一项。
试分别确定最优分配方案,使完成任务的总时间为最少。
人员\任务 | A | B | C | D | E |
---|---|---|---|---|---|
甲 | 25 | 29 | 31 | 42 | 37 |
乙 | 39 | 38 | 26 | 20 | 33 |
丙 | 34 | 27 | 28 | 40 | 32 |
丁 | 24 | 42 | 36 | 23 | 45 |
4.1 (1)问题指派分析
为了确定最优分配方案,使完成任务的总时间最少,我们可以按照以下步骤进行求解:
- 确定任务选择:
- 任务E必须完成,所以必须包含在选择的任务中。
- 在A、B、C、D四项任务中任选三项,共有(43)=4种选择。
- 即选择组合为:{A, B, C, E}、{A, B, D, E}、{A, C, D, E}、{B, C, D, E}。
- 任务分配方案:
- 每个任务选择组合确定后,分配4个人完成这4项任务,计算所有可能的分配情况,选择总时间最少的方案。
- 计算最优方案:
- 对于每一种任务组合,使用计算机算法(如动态规划或组合搜索)来计算所有可能的分配方案,求出总时间最少的方案。
4.2 (1)问题的Python程序
import itertools
import numpy as np
from scipy.optimize import linear_sum_assignment
# 定义任务时间表
time_table = {
'A': [25, 39, 34, 24],
'B': [29, 38, 27, 42],
'C': [31, 26, 28, 36],
'D': [42, 20, 40, 23],
'E': [37, 33, 32, 45]
}
tasks = ['A', 'B', 'C', 'D', 'E']
persons = [0, 1, 2, 3] # 0: 甲, 1: 乙, 2: 丙, 3: 丁
# 任务组合
task_combinations = [
['A', 'B', 'C', 'E'],
['A', 'B', 'D', 'E'],
['A', 'C', 'D', 'E'],
['B', 'C', 'D', 'E']
]
def calculate_assignment_cost(cost_matrix):
row_ind, col_ind = linear_sum_assignment(cost_matrix)
return cost_matrix[row_ind, col_ind].sum(), list(zip(row_ind, col_ind))
best_time = float('inf')
best_combination = None
best_assignment = None
# 存储每个组合的最小总时间
combination_times = {}
for comb in task_combinations:
# 构建成本矩阵
cost_matrix = []
for person in persons:
cost_row = []
for task in comb:
cost_row.append(time_table[task][person])
cost_matrix.append(cost_row)
cost_matrix = np.array(cost_matrix)
# 计算最优分配
time, assignment = calculate_assignment_cost(cost_matrix)
combination_times[tuple(comb)] = time
if time < best_time:
best_time = time
best_combination = comb
best_assignment = assignment
# 输出每个任务组合的最小总时间
print("各任务组合的最小总时间:")
for comb, time in combination_times.items():
print(f"任务组合 {comb}: 最小总时间 {time}分钟")
# 输出最优方案
person_names = ['甲', '乙', '丙', '丁']
assignment_str = {best_combination[task]: person_names[person] for person, task in best_assignment}
print("\n最优方案:")
print(f"最优任务组合: {best_combination}")
print(f"最优分配方案: {assignment_str}")
print(f"最少总时间: {best_time}分钟")
各任务组合的最小总时间:
任务组合 ('A', 'B', 'C', 'E'): 最小总时间 111分钟
任务组合 ('A', 'B', 'D', 'E'): 最小总时间 105分钟
任务组合 ('A', 'C', 'D', 'E'): 最小总时间 106分钟
任务组合 ('B', 'C', 'D', 'E'): 最小总时间 110分钟
最优方案:
最优任务组合: ['A', 'B', 'D', 'E']
最优分配方案: {'B': '甲', 'D': '乙', 'E': '丙', 'A': '丁'}
最少总时间: 105分钟
4.3 (2)问题的指派分析
要解决这个问题,我们可以使用组合与排列的方法生成所有可能的任务分配方案,然后计算每个方案的总时间,选择最小的一个。具体步骤如下:
对4个人中的每一个考虑分配两个任务,其余三个人各分配一个任务,共有(52)=10。
生成新的时间矩阵,对于每一个人承担两个任务的情况,计算出所有可能的任务分配组合。
对每个生成的时间矩阵,使用匈牙利算法(Hungarian Algorithm)来求解最优任务分配问题。
比较所有情况的最小总时间,找到最优分配方案。
4.4(2)问题的Python程序
import numpy as np
from scipy.optimize import linear_sum_assignment
# 给定的时间表
time_table = {
'A': [25, 39, 34, 24],
'B': [29, 38, 27, 42],
'C': [31, 26, 28, 36],
'D': [42, 20, 40, 23],
'E': [37, 33, 32, 45]
}
tasks = ['A', 'B', 'C', 'D', 'E']
persons = ['甲', '乙', '丙', '丁'] # 0: 甲, 1: 乙, 2: 丙, 3: 丁
# 将time_table转换为二维数组
time_matrix = np.array([time_table[task] for task in tasks]).T
# 创建新矩阵并输出
new_matrices = []
for i in range(time_matrix.shape[0]):
new_row = time_matrix[i, :]
new_matrix = np.vstack([time_matrix, new_row])
new_matrices.append((new_matrix, i))
# 对new_matrices进行指派问题求解并打印每个最优值
best_total_time = float('inf')
best_assignment = None
best_matrix = None
best_row_index = None
for idx, (matrix, row_index) in enumerate(new_matrices):
row_ind, col_ind = linear_sum_assignment(matrix)
total_time = matrix[row_ind, col_ind].sum()
print(f"新矩阵 {idx + 1} 的最优值: {total_time} 分钟")
if total_time < best_total_time:
best_total_time = total_time
best_assignment = list(zip(row_ind, col_ind))
best_matrix = matrix
best_row_index = row_index
# 输出最优指派方案和最小值
print("\n最优指派方案:")
for person, task in best_assignment:
person_name = persons[person] if person < len(persons) else '完成两个任务的人员'
task_name = tasks[task] if task < len(tasks) else '额外的任务'
time_spent = best_matrix[person, task]
print(f"{person_name} 负责任务 {task_name}, 所需时间: {time_spent} 分钟")
print(f"最小总时间: {best_total_time} 分钟")
print(f"添加的行: {best_row_index+1},即完成两个任务的人员: {persons[best_row_index]}")
新矩阵 1 的最优值: 135 分钟
新矩阵 2 的最优值: 131 分钟
新矩阵 3 的最优值: 133 分钟
新矩阵 4 的最优值: 134 分钟
最优指派方案:
甲 负责任务 B, 所需时间: 29 分钟
乙 负责任务 C, 所需时间: 26 分钟
丙 负责任务 E, 所需时间: 32 分钟
丁 负责任务 A, 所需时间: 24 分钟
完成两个任务的人员 负责任务 D, 所需时间: 20 分钟
最小总时间: 131 分钟
添加的行: 2,即完成两个任务的人员: 乙
练习5
某航空公司经营 A、B、C 三个城市的航线,这些航线每天班次起飞与到达时间如下表。 设飞机在机场停留的损失费用大致与停留时间的平方成正比。又每架飞机从降落 到下班起飞至少需2小时准备时间,航班安排信息见下表,试决定一个使停留 费用损失为最小的分配飞行方案。
航班号 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
起飞城市 | A | A | A | A | A | B | B | B | C | C | B | B | C | C |
起飞时间 | 9:00 | 10:00 | 15:00 | 20:00 | 22:00 | 4:00 | 11:00 | 15:00 | 7:00 | 15:00 | 13:00 | 18:00 | 15:00 | 7:00 |
到达城市 | B | B | B | C | C | A | A | A | A | A | C | C | B | B |
到达时间 | 12:00 | 13:00 | 18:00 | 24:00:00 | 2:00 (次日) | 7:00 | 14:00 | 18:00 | 11:00 | 19:00 | 18:00 | 23:00 | 20:00 | 12:00 |
import numpy as np
from scipy.optimize import linear_sum_assignment
# 航班数据
flights = [
{"flight": 101, "depart_city": "A", "depart_time": 9, "arrive_city": "B", "arrive_time": 12},
{"flight": 102, "depart_city": "A", "depart_time": 10, "arrive_city": "B", "arrive_time": 13},
{"flight": 103, "depart_city": "A", "depart_time": 15, "arrive_city": "B", "arrive_time": 18},
{"flight": 104, "depart_city": "A", "depart_time": 20, "arrive_city": "C", "arrive_time": 24},
{"flight": 105, "depart_city": "A", "depart_time": 22, "arrive_city": "C", "arrive_time": 26},
{"flight": 106, "depart_city": "B", "depart_time": 4, "arrive_city": "A", "arrive_time": 7},
{"flight": 107, "depart_city": "B", "depart_time": 11, "arrive_city": "A", "arrive_time": 14},
{"flight": 108, "depart_city": "B", "depart_time": 15, "arrive_city": "A", "arrive_time": 18},
{"flight": 109, "depart_city": "C", "depart_time": 7, "arrive_city": "A", "arrive_time": 11},
{"flight": 110, "depart_city": "C", "depart_time": 15, "arrive_city": "A", "arrive_time": 19},
{"flight": 111, "depart_city": "B", "depart_time": 13, "arrive_city": "C", "arrive_time": 18},
{"flight": 112, "depart_city": "B", "depart_time": 18, "arrive_city": "C", "arrive_time": 23},
{"flight": 113, "depart_city": "C", "depart_time": 15, "arrive_city": "B", "arrive_time": 20},
{"flight": 114, "depart_city": "C", "depart_time": 7, "arrive_city": "B", "arrive_time": 12},
]
# 将时间转化为24小时制,处理跨越0点的情况
def convert_time(time):
if time >= 24:
return time - 24
return time
for flight in flights:
flight["arrive_time"] = convert_time(flight["arrive_time"])
# 初始化成本矩阵
n = len(flights)
cost_matrix = np.full((n, n), np.inf)
# 计算成本矩阵
for i, flight_i in enumerate(flights):
for j, flight_j in enumerate(flights):
if flight_i["arrive_city"] == flight_j["depart_city"]:
stay_time = flight_j["depart_time"] - flight_i["arrive_time"]
if stay_time < 0: # 跨越0点的情况
stay_time += 24
if stay_time >= 2: # 至少需要2小时准备时间
cost_matrix[i][j] = stay_time ** 2
# 求解最小成本分配
row_ind, col_ind = linear_sum_assignment(cost_matrix)
min_cost = cost_matrix[row_ind, col_ind].sum()
# 输出结果
solution = [(flights[row]["flight"], flights[col]["flight"]) for row, col in zip(row_ind, col_ind)]
flight_schedule = {}
for row, col in zip(row_ind, col_ind):
if flights[row]["flight"] in flight_schedule:
flight_schedule[flights[row]["flight"]].append(flights[col]["flight"])
else:
flight_schedule[flights[row]["flight"]] = [flights[col]["flight"]]
print("Optimal Flight Assignment:")
for flight, next_flights in flight_schedule.items():
print(f"Flight {flight} -> Next Flights: {next_flights}")
print("Minimum Cost:", min_cost)
Flight 101 -> Next Flights: [108]
Flight 102 -> Next Flights: [106]
Flight 103 -> Next Flights: [107]
Flight 104 -> Next Flights: [113]
Flight 105 -> Next Flights: [110]
Flight 106 -> Next Flights: [102]
Flight 107 -> Next Flights: [104]
Flight 108 -> Next Flights: [105]
Flight 109 -> Next Flights: [103]
Flight 110 -> Next Flights: [101]
Flight 111 -> Next Flights: [114]
Flight 112 -> Next Flights: [109]
Flight 113 -> Next Flights: [111]
Flight 114 -> Next Flights: [112]
Minimum Cost: 1748.0
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
2023-06-09 非线性规划——习题解答(七)
2023-06-09 非线性规划——惩罚函数外点法(六)
2023-06-09 非线性规划——库恩塔克KTT条件(五)
2023-06-09 非线性规划——拉格朗日最优化方法(四)