如何基于行列条件填充排班矩阵(已更新2次)
解决R语言排班规划的约束问题
针对你的需求,我们可以分月份处理休息日分配,确保每个工人每月休息8天,同时每天恰好8人在岗。下面是完整的实现步骤和代码:
步骤说明
- 拆分日期为对应月份,明确每个月的天数(2023年2月28天,3月31天)
- 对每个月份单独处理:先给每个工人随机分配8个休息日,再调整每日在岗人数至要求的8人,同时不破坏每个工人的休息天数限制
完整代码
# 初始参数 n <- 12 # 工人数 np <- 8 # 每日在岗人数 r <- 8 # 每人每月休息天数 vday <- seq(as.Date('2023-02-01'), as.Date('2023-03-31'), by = 1) mm <- matrix(data = NA, nrow = n, ncol = length(vday)) dimnames(mm)[[2]] <- as.character(vday) # 拆分月份:2023年2月和3月 feb_days <- vday[format(vday, "%Y-%m") == "2023-02"] mar_days <- vday[format(vday, "%Y-%m") == "2023-03"] feb_cols <- which(colnames(mm) %in% as.character(feb_days)) mar_cols <- which(colnames(mm) %in% as.character(mar_days)) # 处理单月排班的函数 assign_month_shifts <- function(month_cols, month_day_count) { # 初始化该月矩阵:先全设为在岗(1) month_mat <- matrix(1, nrow = n, ncol = length(month_cols)) # 给每个工人随机分配r个休息日 for (worker in 1:n) { rest_days <- sample(1:length(month_cols), size = r) month_mat[worker, rest_days] <- 0 } # 调整每日在岗人数至np for (day in 1:length(month_cols)) { current_on_duty <- sum(month_mat[, day]) if (current_on_duty > np) { # 在岗人数过多:随机选多余的人改成休息,且确保该工人当月休息天数不超过r excess <- current_on_duty - np eligible_workers <- which(month_mat[, day] == 1 & rowSums(month_mat == 0) < r) if (length(eligible_workers) >= excess) { selected <- sample(eligible_workers, size = excess) month_mat[selected, day] <- 0 } } else if (current_on_duty < np) { # 在岗人数不足:随机选休息的人改成在岗,且确保该工人当月休息天数不低于r deficit <- np - current_on_duty eligible_workers <- which(month_mat[, day] == 0 & rowSums(month_mat == 1) < (month_day_count - r)) if (length(eligible_workers) >= deficit) { selected <- sample(eligible_workers, size = deficit) month_mat[selected, day] <- 1 } } } return(month_mat) } # 分配2月和3月的班次 mm[, feb_cols] <- assign_month_shifts(feb_cols, length(feb_days)) mm[, mar_cols] <- assign_month_shifts(mar_cols, length(mar_days)) # 验证约束是否满足 # 检查每个工人每月休息天数 feb_rest_counts <- rowSums(mm[, feb_cols] == 0) mar_rest_counts <- rowSums(mm[, mar_cols] == 0) cat("2月每人休息天数:", feb_rest_counts, "\n") cat("3月每人休息天数:", mar_rest_counts, "\n") # 检查每日在岗人数 daily_on_duty <- colSums(mm) cat("每日在岗人数:", daily_on_duty, "\n")
代码解释
- 月份拆分:将总日期拆分为2月和3月,确保每月的休息天数计算独立
- 初始休息日分配:先给每个工人随机选8天休息,保证每人每月基础休息天数达标
- 每日人数调整:针对不符合在岗人数要求的日期,在不违反工人每月休息天数限制的前提下,调整人员的在岗/休息状态,确保每天恰好8人在岗
- 验证环节:最后输出每个工人的月休息天数和每日在岗人数,确认所有约束都被满足
内容的提问来源于stack exchange,提问作者user007
相关产品推荐
相关产品推荐

