现有实习生若干,正式员工若干。需要进行实验室值班。
条件1:白天8轮岗,夜晚6轮岗。
条件2:白班每班需要两个人值班(称之为流动班),夜晚需要三个人值班(有一人需在工位等待,称之为坐班。其他俩人为流动班)。
条件3:实习生自己不能单独在流动班,需要正式员工陪同。|
条件4:一个人不能同一天轮到三次班。
条件5:白天不能站两班,晚上不能站两班。
条件6:一周内,同一个人尽量不能同一个时间班。
条件7:每个人都得坐班。
条件8:白班的下午值班过的人,不能再值夜班
条件9:值夜班的人,不能值第二天上午的班。
条件10:不能连续两天同一个人值夜班
条件11:科室可能会发生人员变动
条件12:读取名单表,根据名单表排序
条件13:人员变动后也需要输出排班情况
条件14:每个人的排岗次数之差不能大于2次
部分实例代码:
import random
import pandas as pd
# 读取 CSV 文件
file_path = "people_names.csv" #
data = pd.read_csv(file_path)
# 假设 CSV 文件中有两列:老人 和 新人,分别表示老人的名字和新人的名字
seniors = data['Elderly'].dropna().tolist() # 删除空值并转换为列表
newbies = data['Newcomers'].dropna().tolist() # 删除空值并转换为列表
# 合并所有人员
all_people = seniors + newbies
# 一周时间
days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
# 初始化值班表
schedule = []
# 初始化轮值记录和总排岗次数记录
inner_shift_tracker = {person: 0 for person in all_people} #
total_shift_tracker = {person: 0 for person in all_people} # 总排岗记录
# 最大排岗次数限制
MAX_SHIFTS = 11
# 函数:分配白天的班次(严格遵守外班规则和排岗限制)
def assign_day_shifts(people, day):
day_shifts = []
white_shifted = [] # 用来记录当天排了白班的人员
for round_num in range(8): # 每天8轮白班
print(f"\n------ {day} 白班 第 {round_num + 1} 轮 ------")
# 筛选未达到最大班次限制的候选人
valid_people = [p for p in people if total_shift_tracker[p] < MAX_SHIFTS]
print(f"有效的候选人: {valid_people}")
# 确保每轮有至少1名老人
seniors_available = [p for p in valid_people if p in seniors]
newbies_available = [p for p in valid_people if p in newbies]
if len(seniors_available) < 1:
raise ValueError(f"无法满足白班外班规则(至少1名老人),当前可用老人不足!")
# 优先选择1位老人和1位新人,如果新人足够
if len(newbies_available) > 0:
senior = seniors_available[0] # 选择1位老人
newbie = random.choice(newbies_available) # 随机选择1位新人
shift = [senior, newbie]
else:
# 如果没有足够的新人,则选择2位老人
if len(seniors_available) >= 2:
shift = random.sample(seniors_available, 2) # 两人均为老人
else:
raise ValueError("没有足够的老人分配班次!")
# 更新总排岗次数
for person in shift:
total_shift_tracker[person] += 1
# 打印分配结果
print(f"分配的人员: {shift}")
# 添加到班次表,并移除已分配人员
day_shifts.append(shift)
white_shifted.extend(shift) # 记录已经排白班的人员
people = [p for p in people if p not in shift]
# 打印已分配白班的人员
print(f"已排白班的人员: {white_shifted}")
return day_shifts, white_shifted
# 分配班次
for day in days:
people_for_day = all_people[:] # 每天重置人员列表
day_shifts, white_shifted = assign_day_shifts(people_for_day, day)
night_shifts = assign_night_shifts(people_for_day, day, white_shifted)
for i, shift in enumerate(day_shifts):
schedule.append([day, "白天", i + 1, "无", shift[0], shift[1]])
for i, shift in enumerate(night_shifts):
schedule.append([day, "晚上", i + 1, shift[0], shift[1], shift[2]])
# 转换为 DataFrame
columns = ["日期", "班次类型", "班次编号", "内班", "流动班1", "流动班2"]
df_schedule = pd.DataFrame(schedule, columns=columns)
# 生成排班表之后,交换排班次数最多的人的班次
def balance_shift_distribution(schedule, total_shift_tracker, max_shifts=9):
# 获取总排班次数最多和最少的人
max_shifts_person = max(total_shift_tracker, key=total_shift_tracker.get)
min_shifts_person = min(total_shift_tracker, key=total_shift_tracker.get)
while total_shift_tracker[max_shifts_person] > max_shifts:
# 找到排班次数最多的人和排班次数最少的人
max_shifts_person = max(total_shift_tracker, key=total_shift_tracker.get)
min_shifts_person = min(total_shift_tracker, key=total_shift_tracker.get)
# 找出排班次数最多的人的班次,并进行交换
for shift in schedule:
if max_shifts_person in shift[4:] and min_shifts_person not in shift[4:]:
# 找到包含最多排班人员的班次,并将其与最少排班人员的班次交换
shift[4:] = [min_shifts_person if person == max_shifts_person else person for person in shift[4:]]
total_shift_tracker[max_shifts_person] -= 1
total_shift_tracker[min_shifts_person] += 1
break # 交换完一个班次后就跳出
return schedule
# 在生成排班表之后调用函数
schedule = balance_shift_distribution(schedule, total_shift_tracker)
# 转换为 DataFrame
df_schedule = pd.DataFrame(schedule, columns=columns)
# 保存为 Excel
output_file = "A1.xlsx"
df_schedule.to_excel(output_file, index=False)
print(f"值班表已生成并保存到 {output_file}")