指派问题(匈牙利算法)

问题描述:

在生活中经常遇到这样的问题,某单位需完成n项任务,恰好有n个人可承担这些任务。由于每人的专长不同,各人完成任务不同(或所费时间),效率也不同。于是产生应指派哪个人去完成哪项任务,使完成n项任务的总效率最高(或所需总时间最小)。这类问题称为指派问题或分派问题。
指派问题也是0-1规划,线性规划用到的是 官网scipy.optimize库函数。
示例: cost matrix = [ [1  4 3], [2 0 5], [3 2 2]]
python 解决方案中,用到的是scipy.optimize.linear_sum_assignment(cost_matrix)
代码实现:
from scipy.optimize import linear_sum_assignment
 
cost =np.array([[4,1,3],[2,0,5],[3,2,2]])
row_ind,col_ind=linear_sum_assignment(cost)
print(row_ind)#开销矩阵对应的行索引
print(col_ind)#对应行索引的最优指派的列索引
print(cost[row_ind,col_ind])#提取每个行索引的最优指派列索引所在的元素,形成数组
print(cost[row_ind,col_ind].sum())#数组求和  

输出:
[0 1 2]

[1 0 2] 

[1 2 2] 

5

转载于:https://www.cnblogs.com/ylHe/p/9287384.html

### 匈牙利算法求解指派问题的详细步骤 #### 1. 初始准备 在应用匈牙利算法之前,需将指派问题转化为个标准形式。假设存在 \( n \times n \) 的成本矩阵 \( C = (c_{ij}) \),其中 \( c_{ij} \) 表示第 \( i \) 名工完成第 \( j \)任务的成本。 为了简化计算,通常先对矩阵进行预处理,使其满足某些特定性质。具体来说,可以通过以下方式减少初始矩阵中的数值大小: - 对每行减该行的最小值; - 接着对每列减该列的最小值[^3]。 这样可以确保最终矩阵中至少有部分元素变为零,从而便于后续操作。 #### 2. 寻找独立零元 接下来的目标是从修改后的矩阵里找出组相互间不存在冲突关系(即位于不同行列位置上的)零元组合。这步可通过直观扫描或者更复杂的逻辑判断来达成。如果恰好能找到数目正好等于矩阵维度的组这样的零,则可以直接得出结论并停止整个流程;否则继续执行下步骤[^3]。 #### 3. 覆盖所有零元所需的最少线条数判定 当未能次性获得足够的独立零单元,就需要尝试用尽可能少水平或垂直方向上线条把所有的零都遮挡住。这过程中涉及到反复迭代标记未被充分使用的行与关联列之间的交互作用,直到不能再增加新的有效标志为止。最后依据剩余未被打勾记录下来的那些行以及已被标注过的列绘制相应数量的横竖分割线形成覆盖体系。 #### 4. 修改当前状态下的数据结构 旦发现现有条件下无法仅依靠简单选取就能凑齐所需规模的独立试探点集合之后,便要重新审视全局布局,并采取措施降低整体代价函数值。具体做法包括但不限于定位到尚未受到任何条先前划定界限影响区域内的极小成员代表量级差额Δ,进而实施如下更新策略: - 将未受保护部位的所有成分统扣除掉这个差异额度; - 同样幅度提升处于双重防护交叠地带里的各项指标; - 维持单层屏障庇护对象现状不变[^3]。 随后返回至第阶段再次检验是否存在符合条件的新候选者群组。 --- ### Python实现代码示例 以下是基于上述原理编写的个简易版本程序框架供参考学习使用: ```python import numpy as np def hungarian_algorithm(cost_matrix): # Step 1: Subtract row minima from each row and column minima from each column. cost_matrix -= cost_matrix.min(axis=1, keepdims=True) cost_matrix -= cost_matrix.min(axis=0) n = cost_matrix.shape[0] mask_matrix = np.zeros_like(cost_matrix, dtype=bool) while True: # Step 2: Find a maximum matching using zeros in the matrix. matched_rows, matched_cols = [], [] for r in range(n): if not any(mask_matrix[r]): col_indices_zeroes = list(np.where(~mask_matrix[:, :] & (cost_matrix[r]==0))[0]) if len(col_indices_zeroes)>0: chosen_col_index = col_indices_zeroes[0] masked_row_count_before_addition=sum([int(x)for x in mask_matrix[chosen_col_index]]) mask_matrix[r][chosen_col_index]=True after_masked_sum=sum([int(y)for y in mask_matrix[: , chosen_col_index ]]) if(after_masked_sum>masked_row_count_before_addition):continue matched_rows.append(r);matched_cols.append(chosen_col_index) unmatched_rows=[i for i in range(len(matched_rows))if sum(mask_matrix[i])<len(set(matched_cols))] covered_columns=list();covered_rows=list() def cover_zeros(): marked_rows=set(unmatched_rows.copy()) newly_marked_cols=set() while(True): new_marks=False temp_set={j for idx,j in enumerate(matched_cols)if idx in marked_rows} difference=newly_marked_cols.symmetric_difference(temp_set) if(difference!=set()): newly_marked_cols|=difference; new_marks=True; next_unmarked_rows={r for r,c in zip(range(len(matched_rows)),matched_cols)if(c in newly_marked_cols)} diff_between_sets=next_unmarked_rows-marked_rows if(diff_between_sets != set()): marked_rows |=diff_between_sets ; new_marks=True; elif(new_marks==False):break return(list(marked_rows),list(newly_marked_cols)) covered_rows,covered_columns=cover_zeros(); non_covered_elements=np.array([[value for index,value in enumerate(row)if(index not in covered_columns)]for ind,row in enumerate(cost_matrix)if(ind not in covered_rows)]) minimum_non_covered_value=min(non_covered_elements.flatten())if len(non_covered_elements)!=0 else None if(minimum_non_covered_value is None or all([(sum((~mask_matrix).astype(int)[row,:])==0 )forall rowinrange(n)]) ): break cost_matrix-=minimum_non_covered_value*(np.ones(shape=(n,n))-np.add.outer(*[(a*b)+b*a for a,b in [(np.eye(n)[covered_rows],np.ones((n,))*(not bool(i)))for i in covered_rows]])) result_pairs=[[i,j]for i,j inkzip(matched_rows,matched_cols)] total_cost=sum([cost_matrix[pair[0]][pair[1]]foreach pairinresult_pairs]) return(result_pairs,total_cost) # Example Usage example_matrix = np.array([ [8, 6, 7], [6, 7, 8], [7, 8, 6] ]) pairs,assignments=hungarian_algorithm(example_matrix.copy()) print("Optimal Assignments:", pairs,"with Total Cost of", assignments ) ``` --- ### 结果解释 通过运行以上脚本即可获取指定输入情形下最佳员安排计划及其对应累积开销额。此方法不仅适用于小型案例研究同样也适合较大规模的实际应用场景之中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值