【问题标题】:Optimisation in R - giving each occasion a group leader, every person should be chosen at least onceR 中的优化 - 给每个场合一个组长,每个人都应该至少被选择一次
【发布时间】:2020-11-03 15:07:42
【问题描述】:

我正在尝试为我的课程制定时间表。 我有38个学生。我将在 11 (5+6) 次(BES 和 PBL)上见到他们。

对于每个场合,它们被随机分为 8 组,从而产生 6 组 5 和 2 组 4。

对于每个场合,我都想选择一名学生来领导该小组——一位领导者。理想情况下,我希望每个学生都成为一个小组的负责人。

我最多通过以下方式获得36个独特的领导者。 有没有办法在 R 中运行优化,以便我想要一个最大化唯一领导者数量的目标函数?我的猜测是,目前的限制是随机抽样分组的方式。

set.seed(13)

studentlist <- data.frame(Name=as.character(c(paste0("A",seq(1:38)))))

studentlist$PBL1 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                 labels=paste0(1:8)))

studentlist$PBL2 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$PBL3 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$PBL4 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$PBL5 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$BES1 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$BES2 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$BES3 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$BES4 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$BES5 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))

studentlist$BES6 <- sample(factor(rep(1:8, length.out=nrow(studentlist)), 
                                  labels=paste0(1:8)))
studentlist <- as.data.frame(studentlist)

BES_leaders <- studentlist %>% select(Name, BES1:BES6) %>%  pivot_longer(cols = "BES1":"BES6",names_to = "Occassion",values_to = "Group")

#initiate i to count set.seed
i <- 1
#initialise best solution
BES_leaders3 <-  data.frame()

while(length(unique(BES_leaders2$Leader))<=36) {
  set.seed(i)
  return.i <- i
  
  BES_leaders_2 <- BES_leaders %>% nest_by(Occassion,Group) %>% mutate(Leader= sample(c(data$Name),1,replace = FALSE)) %>% select(Occassion,Group,Leader)
  BES_leaders2 <- as.data.frame(BES_leaders_2)
  
  BES_leaders3 <- if(length(unique(BES_leaders2$Leader)) > length(unique(BES_leaders3$Leader))){BES_leaders2} else {BES_leaders3}
  i <- i+1
  print(length(unique(BES_leaders3$Leader)))
}

【问题讨论】:

  • 不清楚你想要什么,你的数据代表什么。 IE。什么是场合,什么是领导者?您是否为每个场合随机生成学生集群?您的问题确实读起来像带有边约束的优化分配问题。 IE。谁被分配到什么的限制。这些问题可以在 R 中使用整数编程来解决。有软件包可以做到这一点。也许你可以为你的问题定义增加一些清晰度。
  • @SteveM 为了清晰起见,我进行了编辑。现在读起来更好了吗?
  • 看起来你有 6 组 x 11 次 = 66 组。那么,为什么不将 38 名学生一次分配给 66 个小组中的 38 个作为领导,然后将剩余的 37 名学生随机分配给分配领导的小组,而所有 38 名学生的领导是谁并不重要?
  • 以上更正。我看到创建的不是 6 个而是 8 个组,所以总共 88 个组。因此,如果您有 38 名学生,您可以将每个学生指定为两次领导,即 88 项特定领导分配中的 76 项。
  • @SteveM,您能否在回答问题时提供一个示例,以便我将其标记为可能的解决方案?

标签: r optimization while-loop dplyr sampling


【解决方案1】:

我们可以将其表述为一个优化问题:

引入符号:

   i : set of occasions (11)
   s : set of students (38)
   g : set of groups (8)   
   

计算

  gsi[g,s,i] = 1 if student s is in group g at occasion i
               0 otherwise
  this is calculated from your studentlist 

定义变量:

  leader[i,s] = 1 if student s is leader at occasion i
                0 otherwise
  countTimes[s] : number of times student s is a leader
  max : max(countTimes[s])
  min : min(countTimes[s])

构建模型:

  minimize max - min
  subject to 
     sum(s, gsi[g,s,i]*leader[i,s]) = 1  for all i,g  
     countTimes[s] = sum(i,leader[i,s])
     countTimes[s] <= max    for all s
     countTimes[s] >= min    for all s 

对我来说,这给出了:

----     95 PARAMETER lead  

                     A1          A2          A3          A4          A5          A6          A7          A8          A9

PBL2.group4                                                                       1
PBL3.group6                                   1
PBL4.group2                                                           1
PBL4.group4                                                                                   1
PBL4.group7           1
PBL5.group6                       1
PBL5.group7                                               1
BES1.group1                                                                                                           1
BES1.group4                                                           1
BES1.group8           1
BES2.group3                                                                                               1
BES2.group4                                               1
BES3.group4                                                                                   1
BES3.group7                                                                                                           1
BES4.group5           1
BES4.group6                                   1
BES4.group8                                                                                               1
BES5.group1                                               1
BES5.group2                                                           1
BES5.group3                                                                       1
BES6.group1                       1
BES6.group6                                                                                                           1

          +         A10         A11         A12         A13         A14         A15         A16         A17         A18

PBL1.group1                                                                       1
PBL1.group2           1
PBL1.group4                                                           1
PBL1.group8                                                                                               1
PBL2.group1                                                                                                           1
PBL2.group7                                                                                   1
PBL3.group4                                                                                               1
PBL5.group2                       1
PBL5.group4                                               1
PBL5.group5                                                                                               1
BES1.group7                                               1
BES2.group1                                                                                   1
BES2.group5                                                           1
BES4.group1                       1
BES4.group4                                   1
BES5.group5                                   1
BES5.group8           1
BES6.group7                                                                       1
BES6.group8                                                                                                           1

          +         A19         A20         A21         A22         A23         A24         A25         A26         A27

PBL1.group3                                                           1
PBL1.group5                                               1
PBL2.group2                                                                                                           1
PBL2.group3           1
PBL2.group8                                               1
PBL3.group2                                                                                   1
PBL3.group7                       1
PBL4.group1                                                                                                           1
PBL4.group3                                                                                               1
PBL4.group6                       1
PBL5.group1                                                                       1
PBL5.group3                                                           1
BES3.group1                                   1
BES3.group2                                                                                                           1
BES4.group3                                                                                   1
BES4.group7                       1
BES5.group4                                                                                               1
BES5.group6                                   1
BES6.group2                                   1
BES6.group4                                                                       1
BES6.group5           1

          +         A28         A29         A30         A31         A32         A33         A34         A35         A36

PBL1.group7                                                                                               1
PBL2.group5                                               1
PBL2.group6           1
PBL3.group1                                                           1
PBL3.group3                                               1
PBL4.group5                                   1
PBL4.group8                                                                                   1
PBL5.group8                                               1
BES1.group2                                                                                                           1
BES1.group3                                                                       1
BES1.group5                                   1
BES1.group6                       1
BES2.group2                                                                                                           1
BES2.group6                                                                       1
BES2.group7                       1
BES2.group8                                                                                   1
BES3.group5           1
BES3.group6                                                                                               1
BES3.group8                                                                       1
BES4.group2                                                           1
BES5.group7                                                                                                           1

          +         A37         A38

PBL1.group6                       1
PBL3.group5                       1
PBL3.group8           1
BES3.group3                       1
BES6.group3           1

  

每个学生都是领导者的两到三倍。我没有使用 R,而是使用商业工具。我会看看我是否可以在 R 中做到这一点。

R 实现可能如下所示:

library(dplyr)
library(ROI)
library(ROI.plugin.glpk)
library(ompr)
library(ompr.roi)

numStudents <- 38
numGroups <- 8
numMeetings <- 11


# code from question
set.seed(13)
studentlist <- data.frame(Name=as.character(c(paste0("A",seq(1:38)))))
studentlist$PBL1 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$PBL2 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$PBL3 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$PBL4 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$PBL5 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$BES1 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$BES2 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$BES3 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$BES4 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$BES5 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist$BES6 <- sample(factor(rep(1:8, length.out=nrow(studentlist)),labels=paste0(1:8)))
studentlist <- as.data.frame(studentlist)
studentlist

# form 3d binary matrix  
gsi <- array(0L,dim=c(numGroups,numStudents,numMeetings))
for (s in 1:numStudents)
  for (i in 1:numMeetings) { 
    g <- studentlist[s,i+1]  
    gsi[g,s,i] = 1L
  }


result <- MIPModel() %>%
  add_variable(leader[i,s], i=1:numMeetings, s=1:numStudents, type = "binary") %>%
  add_variable(count[s], s=1:numStudents) %>%
  add_variable(max) %>%
  add_variable(min) %>%
  set_objective(max-min, "min") %>%
  add_constraint(sum_expr(gsi[g,s,i]*leader[i,s], s=1:numStudents) == 1, i=1:numMeetings, g=1:numGroups) %>%
  add_constraint(count[s] == sum_expr(leader[i,s], i=1:numMeetings), s=1:numStudents) %>%
  add_constraint(count[s] <= max, s=1:numStudents) %>%
  add_constraint(count[s] >= min, s=1:numStudents) %>%
  add_constraint(min >= 1) %>%
  # too slow to prove optimality. We stop after 200 seconds.
  solve_model(with_ROI(solver="glpk", verbose=T, tm_limit=20000)) %>% 
  get_solution(leader[i,s])  %>%
  filter(value > 0) 

result2 <- matrix(0L,nrow=numStudents,ncol=numMeetings)
for (k in 1:nrow(result)) {
  i <- result$i[k]
  s <- result$s[k]
  result2[s,i] <- 1
}
rownames(result2) <- studentlist$Name
colnames(result2) <- colnames(studentlist)[-1]
result2

求解器不够强大,无法证明最优性,但我们应该在(远)小于我们 200 秒的时间限制(目标=1)内找到最优解。 result2 输出如下所示:

> result2
    PBL1 PBL2 PBL3 PBL4 PBL5 BES1 BES2 BES3 BES4 BES5 BES6
A1     0    1    0    1    0    0    1    0    0    0    0
A2     1    0    0    0    0    0    0    0    1    1    0
A3     0    0    1    0    0    0    0    1    0    1    0
A4     1    0    1    1    0    0    0    0    0    0    0
A5     1    0    0    0    1    0    0    1    0    0    0
A6     0    0    0    1    0    0    0    0    1    1    0
A7     0    0    0    0    0    0    1    1    1    0    0
A8     0    1    0    0    1    1    0    0    0    0    0
A9     1    0    0    0    1    0    0    0    0    0    0
A10    0    1    1    0    0    0    0    0    0    0    0
A11    0    0    1    0    0    0    0    1    0    0    0
A12    1    0    0    0    0    0    0    0    1    0    0
A13    0    0    0    1    0    1    0    0    0    0    0
A14    0    0    0    0    0    0    1    1    0    0    0
A15    0    0    0    0    0    0    0    0    1    0    1
A16    0    1    0    0    0    1    0    0    0    0    1
A17    0    1    0    0    1    0    0    0    0    0    0
A18    0    0    0    0    0    0    1    0    0    1    0
A19    0    0    0    0    1    0    0    0    0    1    0
A20    1    0    0    0    0    1    0    0    0    0    0
A21    0    0    0    0    0    0    1    0    0    0    1
A22    0    1    0    0    0    1    0    0    0    0    0
A23    0    0    0    0    0    0    0    0    0    1    1
A24    0    0    0    0    1    0    0    0    0    0    1
A25    0    0    0    0    0    1    0    0    0    0    1
A26    0    0    0    0    0    0    1    0    1    0    0
A27    0    0    1    0    0    0    0    1    0    0    0
A28    0    0    0    1    0    0    0    1    0    1    0
A29    1    0    0    0    0    0    0    0    0    0    1
A30    1    0    0    1    0    0    1    0    0    0    0
A31    0    0    0    0    0    1    0    1    0    0    0
A32    0    0    1    0    0    0    1    0    0    0    0
A33    0    0    1    0    0    0    0    0    1    0    0
A34    0    0    1    0    0    0    0    0    0    1    0
A35    0    1    0    0    1    0    0    0    1    0    0
A36    0    0    0    1    0    0    0    0    0    0    1
A37    0    1    0    0    0    1    0    0    0    0    0
A38    0    0    0    1    1    0    0    0    0    0    0

【讨论】:

    【解决方案2】:

    这是一个简化的问题,将 5 个领导者分配到 10 个 5 人小组,(每个人做了两次领导)

    m1 <- replicate(10, sample(1:5, 5)) #randomize 10 groups of 5
    leaders <- c(1:5, 1:5) # create the leader top row
    m2 <- rbind(leaders, m1)  # bind the leader row to the random groups
    m3 <- apply(m2, 2, unique) # remove the redundant leaders from each group
    

    除了 76 个小组的 38 名学生之外,您也可以这样做。然后简单地随机分配剩余的 12 个组,任意分配领导者并将它们 cbind 到 m3。

    【讨论】:

      【解决方案3】:

      此解决方案类似于 SteveM 的方法,即按顺序将领导者分配到场合和组中。然而,它明确地处理了两个小组的大小,并在不同的小组和场合随机分配学生。它仅使用基础 R 和 tidyverse。所有学生都被指定为领导者两次或三次。

          library(tidyverse)
          set.seed(13)
          students <- c(paste0("A",seq(1:38))) %>% factor()
          n_students <- length(students)
          n_occasions <- 11
      #  
          n_memb_1 <- 4  # number of members in each of first set of groups
          n_grps_1 <- 6  # number of groups in the first set
          n_memb_2 <- 3  # number of members in each of second set of groups
          n_grps_2 <- 2  # number of groups in the second set
      #
      #  create sequences for groups and member sets
      #
          member_sets <- c(rep(1: n_memb_1, times = n_grps_1), rep(1:n_memb_2, times = n_grps_2))
          group_sets <- c(rep(1:n_grps_1,each = n_memb_1), rep((n_grps_1+1):(n_grps_1 + n_grps_2),each = n_memb_2))
      #
          n_groups <- n_grps_1 + n_grps_2
      #   
      #  make vectors of leaders for all occasions and groups
      #   
         leaders <- unlist(rep(students, ceiling(n_groups*n_occasions/n_students) ))[1:(n_occasions*n_groups)]
      #  make an empty tibble object to collect results  
         group_assign <- tibble()
      #
      #  loop over occasions and collect results into group_assign
      # 
         for( i_oc in 0:(n_occasions-1)) {
        
      # get leaders for this occasion      
            occasion_leaders <- leaders[(i_oc*n_groups+1):((i_oc+1)*n_groups)]
      # make list of students without leaders and randimize them
            members <- setdiff(students, occasion_leaders) %>%
                      sample(n_students - n_groups) 
      # collect into tibble for this occasion and combine with other occasions
             group_assign <-   tibble(occasion = i_oc+1, 
                                      group = group_sets, 
                                     leader = occasion_leaders[group_sets],
                                     member_number = member_sets,
                                     member = members) %>% 
                                bind_rows(group_assign)
        
            }
       #
       # format for display
       #
          group_assign <- group_assign %>% arrange(occasion, group) %>%
                          pivot_wider(names_from = member_number, 
                                     values_from = member,
                                     names_prefix = "Member_")
      

      结果的前10行是:

      # A tibble: 88 x 7
         occasion group leader Member_1 Member_2 Member_3 Member_4
            <dbl> <int> <fct>  <chr>    <chr>    <chr>    <chr>   
                1     1 A1     A32      A11      A13      A18     
                1     2 A2     A21      A14      A24      A30     
                1     3 A3     A12      A27      A25      A35     
                1     4 A4     A23      A36      A20      A9      
                1     5 A5     A33      A16      A26      A38     
                1     6 A6     A31      A34      A15      A22     
                1     7 A7     A28      A17      A37      NA      
                1     8 A8     A29      A19      A10      NA      
                2     1 A9     A38      A4       A2       A1      
                2     2 A10    A27      A32      A34      A30 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-13
        • 2018-06-21
        • 2017-06-09
        • 2020-10-19
        • 2011-05-01
        • 1970-01-01
        • 2014-02-03
        相关资源
        最近更新 更多