運籌學練習Python精解——指派問題

郝hai發表於2024-06-09

練習8

分配甲、乙、丙、丁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

8.1 (1)問題指派分析

為了確定最優分配方案,使完成任務的總時間最少,我們可以按照以下步驟進行求解:

  • 確定任務選擇
    • 任務E必須完成,所以必須包含在選擇的任務中。
    • 在A、B、C、D四項任務中任選三項,共有\(\binom{4}{3} = 4\)種選擇。
      • 即選擇組合為:{A, B, C, E}、{A, B, D, E}、{A, C, D, E}、{B, C, D, E}。
  • 任務分配方案
    • 每個任務選擇組合確定後,分配4個人完成這4項任務,計算所有可能的分配情況,選擇總時間最少的方案。
  • 計算最優方案
    • 對於每一種任務組合,使用計算機演算法(如動態規劃或組合搜尋)來計算所有可能的分配方案,求出總時間最少的方案。

8.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分鐘

8.3 (2)問題的指派分析

要解決這個問題,我們可以使用組合與排列的方法生成所有可能的任務分配方案,然後計算每個方案的總時間,選擇最小的一個。具體步驟如下:

對4個人中的每一個考慮分配兩個任務,其餘三個人各分配一個任務,共有\(\binom{5}{2} = 10\)
生成新的成本矩陣,對於每一個人承擔兩個任務的情況,計算出所有可能的任務分配組合。
對每個生成的成本矩陣,使用匈牙利演算法(Hungarian Algorithm)來求解最優任務分配問題。
比較所有情況的最小總時間,找到最優分配方案。

8.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,即完成兩個任務的人員: 乙

相關文章