我是限制编程的新手。现在我尝试建立每周时间表来管理员工的工作时间并满足劳动力需求。我正在使用ILOG CPLEX并尝试解决问题。我认为这是一个容易出问题但我无法绕过它。这是问题所在:
•我们有多个工人(即47名工人),每个工人都有一些限制(假设工作日最短工作时间,最长工作时间工作时间,工作时间工作时间,工作时间最长工作时间)
•每个工人至少有一项技能来执行任务,具有一定的水平
•我们每天都有一些任务(即10个任务),每天分成48个插槽(0~47)。半小时一个插槽。
•工人只能在同一个时段(半小时)处理一项任务
•一个位置的任务有劳动力需求(可能是2,意味着:需要两个工人在此位置执行此任务),最小需求(可能是1)和最大需求(可能是10) 。
我们如何将工作任务分配给工人以满足需求并最大限度地减少使用的工人数量,并指派更高级别的工人来完成任务?
我只是在学习CP& ILOG CPLEX为期两周。关于如何约束(即最小工作时间)的任何想法? 我写了一些代码如下:
CP cp = new CP();
//objective
INumExpr skillMatrixSum = cp.IntExpr();
//day min work hours and so on
IList<SMAIlogStaffInfo> listStaffInfo = SMAScheduleByILogStaffInfoBusiness.GetStaffInfoList(unitId, startDate, endDate, connectionString);
//skill level
IList<SMAIlogPersonSkillMatrixInfo> listPersonSkillMatrixInfo = SMAScheduleByILogSkillMatrixInfoBusiness.GetPersonSkillMatrixInfoList(unitId, startDate, endDate, connectionString);
//demand for every half hour
IList<SMAIlogDemandInfo> listDemandInfo = SMAScheduleByILogDemandInfoBusiness.GetDemandInfoList(unitId, startDate, endDate, connectionString);
//staff unavailability (i.e. ask for a leave)
IList<SMAIlogUnvailInfo> listUnvailInfo = SMAScheduleByILogUnvailInfoBusiness.GetUnvailInfoList(unitId, startDate, endDate, connectionString);
int nbWorkers = listStaffInfo.Select(t => t.PersonId).Distinct().Count();
IList<string> listPerson = listStaffInfo.Select(t => t.PersonId).ToList();
//possible tasks by skill
List<IIntervalVar> allTasks = new List<IIntervalVar>();
List<IIntervalVar>[] workerTasks = new List<IIntervalVar>[nbWorkers];
for (int w = 0; w < nbWorkers; w++)
{
workerTasks[w] = new List<IIntervalVar>();
}
//schedule by day from sunday to saturday
for (int weekdayIndex = 0; weekdayIndex < 7; weekdayIndex++)
{
IList<SMAIlogDemandInfo> listWeekdayDemandInfo = listDemandInfo.Where(t => t.Weekday == weekdayIndex).ToList();
IList<SMAIlogUnvailInfo> listWeekdayUnvailInfo = listUnvailInfo.Where(t => t.Weekday == weekdayIndex).ToList();
SetScheduleByDay(cp, skillMatrixSum, allTasks, workerTasks, listWeekdayDemandInfo, listWeekdayUnvailInfo, listStaffInfo, listPersonSkillMatrixInfo);
}
//constraint: one worker can not do two tasks at the same time
for (int w = 0; w < nbWorkers; w++)
{
IIntervalSequenceVar seq = cp.IntervalSequenceVar(workerTasks[w].ToArray(), listPerson[w]);
cp.Add(cp.NoOverlap(seq));
}
//Minimize skill Matrix
cp.Add(cp.Minimize(skillMatrixSum));
public static void SetScheduleByDay(CP cp, INumExpr skillMatrixSum, List<IIntervalVar> allTasks, List<IIntervalVar>[] workerTasks,
IList<SMAIlogDemandInfo> listWeekdayDemandInfo, IList<SMAIlogUnvailInfo> listWeekdayUnvailInfo, IList<SMAIlogStaffInfo> listStaffInfo, IList<SMAIlogPersonSkillMatrixInfo> listPersonSkillMatrixInfo)
{
int weekdayIndex = listWeekdayDemandInfo.Select(t => t.Weekday).FirstOrDefault();
int skillCount = listWeekdayDemandInfo.Select(t => t.SkillId).Distinct().Count(); //task count
int nbTasks = listWeekdayDemandInfo.Count(); //weekday demand
int nbWorkers = listStaffInfo.Count();//workers count
//decision variables
IIntervalVar[] tasks = new IIntervalVar[nbTasks];
IIntervalVar[,] taskMatrix = new IIntervalVar[nbTasks, nbWorkers];
ICumulFunctionExpr[] arrayResourcesWorkHours = new ICumulFunctionExpr[nbWorkers];
for (int j = 0; j < nbWorkers; j++)
{
arrayResourcesWorkHours[j] = cp.CumulFunctionExpr();
}
string taskFullName;
string skillId = "";
string personId = "";
int levelId = 0;
int demand = 0;
int slot = 0;
for (int i = 0; i < nbTasks; i++)
{
slot = listWeekdayDemandInfo[i].Slot;
demand = listWeekdayDemandInfo[i].Demand;
if (demand == 0)
{
continue;
}
skillId = listWeekdayDemandInfo[i].SkillId;
List<IIntervalVar> alttasks = new List<IIntervalVar>();
for (int w = 0; w < listStaffInfo.Count(); w++)
{
personId = listStaffInfo[w].PersonId;
levelId = listPersonSkillMatrixInfo.Where(t => t.SkillId == skillId && t.PersonId == personId).ToList().ToList().FirstOrDefault().LevelId;
//this worker can do this task
if (levelId > 0)
{
taskFullName = personId + "-" + weekdayIndex + "-" + slot + "-" + skillId;
IIntervalVar wtask = cp.IntervalVar(1, taskFullName);
wtask.SetOptional(); // SetOptional
alttasks.Add(wtask);
taskMatrix[i, w] = wtask;
workerTasks[w].Add(wtask);
allTasks.Add(wtask);
listStaffInfo[w].listWorkerTasks.Add(wtask);
skillMatrixSum = cp.Sum(skillMatrixSum, cp.Prod(levelId, cp.PresenceOf(wtask)));
}
}
cp.Add(cp.Alternative(tasks[i], alttasks.ToArray()));
}
//here is my problem. the way to generalize cumulative is wrong (DayMin and DayMax,also unavailability ).because tasks is Set Optional
for (int j = 0; j < listStaffInfo.Count; j++)
{
IList<IIntervalVar> listWorkerTasks = listStaffInfo[j].listWorkerTasks;
if (listWorkerTasks.Count == 0)
{
continue;
}
int dayMin = listStaffInfo[j].DayMin;
int dayMax = listStaffInfo[j].DayMax;
IConstraint dayMinConstraint = cp.Ge(arrayResourcesWorkHours[j], dayMin);
cp.Add(dayMinConstraint);
IConstraint dayMaxConstraint = cp.Le(arrayResourcesWorkHours[j], dayMax);
cp.Add(dayMaxConstraint);
INumToNumStepFunction availablityNumStep = cp.NumToNumStepFunction();
IList<SMAIlogUnvailInfo> listWeekdayPersonUnvailInfo = listWeekdayUnvailInfo.Where(t => t.PersonId == personId).ToList();
if (listWeekdayPersonUnvailInfo.Count > 0)
{
for (int k = 0; k < listWeekdayPersonUnvailInfo.Count; k++)
{
int startSlot = listWeekdayPersonUnvailInfo[k].StartSlot;
int endSlot = listWeekdayPersonUnvailInfo[k].EndSlot;
for (int i = 0; i < listWorkerTasks.Count; i++)
{
while (startSlot <= endSlot)
{
availablityNumStep.SetValue(startSlot, startSlot + 1, 0);
//cp.Add(cp.ForbidExtent(listWorkerTasks[i], availablityNumStep));
cp.Add(cp.ForbidStart(listWorkerTasks[i], availablityNumStep));
cp.Add(cp.ForbidEnd(listWorkerTasks[i], availablityNumStep));
startSlot++;
}
}
}
}
}
}
我有一组输入数据如下:
我也有一个输出数据的例子如下: Schedule Output Data