匈牙利算法,或数学如何帮助分配作业

朋友您好! 在本文中,我想谈一谈来自“操作研究”学科的有趣算法,即匈牙利方法以及如何在其帮助下解决分配问题。 我将讨论有关该算法在哪些情况下以及在哪些任务中适用的理论,我将在我的发明示例中逐步分析它,并分享我用R语言实现该代码的适度概述。让我们开始吧!

图片

关于方法的几句话


为了不使用数学术语和定义来描述很多理论,我建议考虑一些用于构造分配问题的选项,并且我想您会立即了解在哪种情况下我们将使用匈牙利方法:

  • 任命雇员的任务。 必须将工人分配到各个职位,以实现最大的效率或最小的工作成本。
  • 将机器分配给生产部门。 分配机器,使机器在生产过程中尽可能地获利,或者将其维护成本降至最低。
  • 估计各种职位空缺的人选。 我们将在下面分析此示例。

如您所见,匈牙利方法适用于许多选项,并且在许多活动领域中也会出现类似的任务。

结果,应该解决该任务,以使一个执行者(人,机器,工具等)只能执行一项工作,而每个工作只能由一个执行者来执行。

解决问题的必要和充分条件是封闭型。 即 当表演者数量=作品数量(N = M)时。 如果不满足此条件,则可以添加虚构的表演者或虚构的作品,其矩阵中的值为零。 这不会影响问题的解决,只会给它提供必要的封闭类型。

逐步算法示例


问题陈述:计划一次重要的科学会议。 为此,您需要设置声音,灯光,图像,注册客人并为表演之间的休息做准备。 此任务有5个组织者。 他们每个人都有对特定工作绩效的某些估算(假设这些估算被设置为对其员工进行审核的算术平均值)。 必须分配组织者,以使其总分最大。 该任务具有以下形式:

图片

如果最大程度地解决了问题(如本例所示),则在矩阵的每一行中都必须找到最大元素,将其从相应行的每个元素中减去,然后将整个矩阵乘以-1。 如果问题已解决到最小,则必须跳过此步骤。

图片

在每一行和每一列中,只有一个选定的零。 (即,选择零时,将不再考虑此行或此列中的其余零)。 在这种情况下,不能这样做:

图片

如果问题已得到最小解决,则需要从此步骤开始 )。 我们将继续解决方案。 矩阵按行归约(在每一行中寻找最小元素,并分别从每个元素中减去它):

图片

因为 由于所有最小元素均为零,因此矩阵未更改。 我们执行以下列的减少:

图片

同样,我们希望在每一列和每一行中只有一个选定的零。 如下所示,在这种情况下是不可能的。 我为如何选择零提供了两种选择,但没有一个能提供理想的结果:

图片

我们将进一步做出决定。 删除包含零元素的行和列( 重要!交叉的数量应该最少 )。 在其余元素中,我们寻找最小值,将其从其余元素(未划掉)中减去,然后添加到位于划掉的行和列的交点处的元素(在此处减去绿色标记的部分;在此处总结金色标记的部分;然后,未涂完的内容-请勿触摸):

图片

如您现在所见,在每一列和每一行中,只有一个选定的零。 我们完成了问题的解决!

图片

在初始表中替换所选零的位置。 因此,我们获得了一个最佳计划,即组织者在工作中分配,并且估计总和最大:

图片

如果您解决了该问题,但仍然无法选择零以使每一列和每一行中只有一个,那么我们将从执行行减少的位置(每行中的最小元素)的位置重复该算法。

编程语言实现R


匈牙利算法是使用递归实现的。 我希望我的代码不会造成麻烦。 首先,您需要编译三个函数,然后开始计算。

用于解决问题的数据取自example.csv文件,其格式为:

图片

该代码已添加到剧透,因为它太大了
#     library(dplyr) # csv  (  -  ;   -  ) table <- read.csv("example.csv",header=TRUE,row.names=1,sep=";") #  unique_index <- hungarian_algorithm(table,T) # cat(paste(row.names(table[as.vector(unique_index$row),])," - ",names(table[as.vector(unique_index$col)])),sep = "\n") #   cat("  -",sum(mapply(function(i, j) table[i, j], unique_index$row, unique_index$col, SIMPLIFY = TRUE))) #____________________  __________________________________ hungarian_algorithm <- function(data,optim=F){ # optim = T,       if(optim==T) { data <- data %>% apply(1,function(x) (x-max(x))*(-1)) %>% t() %>% as.data.frame() optim <- F } #    data <- data %>% apply(1,function(x) x-min(x)) %>% t() %>% as.data.frame() #    zero_index <- which(data==0, arr.ind = T) #  ""  - unique_index <- from_the_beginning(zero_index) #  ""        , .. if(nrow(unique_index)!=nrow(data)) #.. ""  - unique_index <- from_the_end(zero_index) #    ,     if(nrow(unique_index)!=nrow(data)) { #    data <- data %>% apply(2,function(x) x-min(x)) %>% as.data.frame() zero_index <- which(data==0, arr.ind = T) unique_index <- from_the_beginning(zero_index) if(nrow(unique_index)!=nrow(data)) unique_index <- from_the_end(zero_index) if(nrow(unique_index)!=nrow(data)) { #""        (!     ) index <- which(apply(data,1,function(x) length(x[x==0])>1)) index2 <- which(apply(data[-index,],2,function(x) length(x[x==0])>0)) #     min_from_table <- min(data[-index,-index2]) #     data[-index,-index2] <- data[-index,-index2]-min_from_table #  ,        data[index,index2] <- data[index,index2]+min_from_table zero_index <- which(data==0, arr.ind = T) unique_index <- from_the_beginning(zero_index) if(nrow(unique_index)!=nrow(data)) unique_index <- from_the_end(zero_index) #    ""        , .. if(nrow(unique_index)!=nrow(data)) #..    hungarian_algorithm(data,optim) else #  ""  unique_index } else #  ""  unique_index } else #  ""  unique_index } #_________________________________________________________________________________ #__________   ""  -___________ from_the_beginning <- function(x,i=0,j=0,index = data.frame(row=numeric(),col=numeric())){ #  ,      i,   j find_zero <- x[(!x[,1] %in% i) & (!x[,2] %in% j),] if(length(find_zero)>2){ #     i <- c(i,as.vector(find_zero[1,1])) #     j <- c(j,as.vector(find_zero[1,2])) #   data frame (     ) index <- rbind(index,setNames(as.list(find_zero[1,]), names(index))) #         from_the_beginning(find_zero,i,j,index)} else rbind(index,find_zero) } #_________________________________________________________________________________ #__________   ""  -___________ from_the_end <- function(x,i=0,j=0,index = data.frame(row=numeric(),col=numeric())){ find_zero <- x[(!x[,1] %in% i) & (!x[,2] %in% j),] if(length(find_zero)>2){ i <- c(i,as.vector(find_zero[nrow(find_zero),1])) j <- c(j,as.vector(find_zero[nrow(find_zero),2])) index <- rbind(index,setNames(as.list(find_zero[nrow(find_zero),]), names(index))) from_the_end(find_zero,i,j,index)} else rbind(index,find_zero) } #_________________________________________________________________________________ 


程序的结果:

图片
图片


总结


非常感谢您抽出宝贵的时间阅读我的文章。 我将提供下面使用的所有链接。 希望您自己学习了一些新知识或更新了旧知识。 一切成功,祝你好运!

使用资源


1. 风暴 维基百科
2. 其他 好的 网站
3. 为我自己强调这篇文章中的一些信息。

Source: https://habr.com/ru/post/zh-CN422009/


All Articles