R 中的优化 - 给每个场合一个组长,每个人都应该至少被选择一次
Optimisation in R - giving each occasion a group leader, every person should be chosen at least once
我正在尝试为我的 class 创建一个时间表。
我有38个学生。我将在 11 (5+6) 次(BES 和 PBL)中与他们见面。
每次他们都被随机分为 8 组,结果是 6 组 5 人和 2 组 4 人。
对于每一个场合,我想选择一个学生来领导那个小组——一个领导者。我希望每个学生都当过一次小组组长。
我最多通过以下方式获得36个独特的领导者。
有没有办法 运行 在 R 中进行优化,以便我想要一个 objective 函数来最大化唯一领导者的数量?我的猜测是当前的限制是通过随机抽样分组的方式。
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)))
}
这是一个简化的问题,将 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 个组,任意分配领导者并将它们绑定到 m3。
我们可以将其表述为优化问题:
引入符号:
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 秒的时间限制 (objective=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
此解决方案遵循类似 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
我正在尝试为我的 class 创建一个时间表。 我有38个学生。我将在 11 (5+6) 次(BES 和 PBL)中与他们见面。
每次他们都被随机分为 8 组,结果是 6 组 5 人和 2 组 4 人。
对于每一个场合,我想选择一个学生来领导那个小组——一个领导者。我希望每个学生都当过一次小组组长。
我最多通过以下方式获得36个独特的领导者。 有没有办法 运行 在 R 中进行优化,以便我想要一个 objective 函数来最大化唯一领导者的数量?我的猜测是当前的限制是通过随机抽样分组的方式。
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)))
}
这是一个简化的问题,将 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 个组,任意分配领导者并将它们绑定到 m3。
我们可以将其表述为优化问题:
引入符号:
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 秒的时间限制 (objective=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
此解决方案遵循类似 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