我有关于在谷歌或工具中安排的问题。
我正在尝试根据班次需要多少员工来构建调度程序,所以白天我需要 10 个,然后从 17:00 到 20:00(夜班)我可能需要 10% 到 100% 的员工工作。
他们每天至少可以工作 8 小时到 12 小时。 他们可以每周工作至少 45 小时到 60 小时, 他们最多可以连续工作几天,然后他们需要休息一天。
这是我目前所拥有的
public class ShiftSchedulingSat
{
static void Main(string[] args)
{
SolveShiftScheduling();
}
static void SolveShiftScheduling()
{
int numEmployees = 15;
int numWeeks = 1;
//Shift day "0" day off
//Shift day "8" hours for the day
//Shift day "12" hours for the day
var shifts = new[] { "0", "8", "12" };
var numDays = numWeeks * 7;
var numShifts = shifts.Length;
LinearExprBuilder obj = LinearExpr.NewBuilder();
//The Demands needed
var weeklyCoverDemands = new int[][] {
new[] { 12, 12 }, // Monday
new[] { 12, 12 }, // Tuesday
new[] { 12, 12 }, // Wednesday
new[] { 12, 12 }, // Thursday
new[] { 12, 12 }, // Friday
new[] { 12, 12 }, // Saturday
new[] { 12, 12 }, // Sunday
};
var model = new CpModel();
BoolVar[,,] work = new BoolVar[numEmployees, numDays, numShifts];
foreach (int e in Range(numEmployees))
{
foreach (int d in Range(numDays))
{
foreach (int s in Range(numShifts))
{
work[e, d, s] = model.NewBoolVar($"work{e}_{d}_{s}");
}
}
}
// Max 3 days
foreach (int e in Range(numEmployees))
{
foreach (int d in Range(2, numDays-2))
{
foreach (int s in Range(numShifts))
{
model.AddBoolOr(new ILiteral[] { work[e, d - 2, s], work[e, d, s].Not(), work[e, d + 2, s] });
}
}
}
// number of required employees for shift 8 hours.
foreach (int e in Range(numEmployees))
{
var temp = new BoolVar[numEmployees];
foreach (int d in Range(numDays))
{
temp[e] = work[e, d, 1];
}
model.Add(LinearExpr.Sum(temp) == 12);
}
// 1 Type of shift per day.
foreach (int e in Range(numEmployees))
{
foreach (int d in Range(numDays))
{
var temp = new BoolVar[numShifts];
foreach (int s in Range(numShifts))
{
temp[s] = work[e, d, s];
}
model.Add(LinearExpr.Sum(temp) == 1);
}
}
// Objective
model.Maximize(obj);
// Solve model
var solver = new CpSolver();
solver.StringParameters = "num_search_workers:8, log_search_progress: true, max_time_in_seconds:30";
var status = solver.Solve(model);
// Print solution
if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible)
{
Console.WriteLine();
var header = " ";
for (int w = 0; w < numWeeks; w++)
{
header += "M T W T F S S ";
}
Console.WriteLine(header);
foreach (int e in Range(numEmployees))
{
var schedule = "";
foreach (int d in Range(numDays))
{
foreach (int s in Range(numShifts))
{
if (solver.BooleanValue(work[e, d, s]))
{
schedule += shifts[s] + " ";
}
}
}
Console.WriteLine($"worker {e}: {schedule}");
}
Console.WriteLine();
Console.WriteLine("Penalties:");
Console.WriteLine();
Console.WriteLine("Statistics");
Console.WriteLine($" - status : {status}");
Console.WriteLine($" - conflicts : {solver.NumConflicts()}");
Console.WriteLine($" - branches : {solver.NumBranches()}");
Console.WriteLine($" - wall time : {solver.WallTime()}");
}
}
/// <summary>
/// Filters an isolated sub-sequence of variables assigned to True.
/// Extract the span of Boolean variables[start, start + length), negate them,
/// and if there is variables to the left / right of this span, surround the
/// span by them in non negated form.
/// </summary>
/// <param name="works">A list of variables to extract the span from.</param>
/// <param name="start">The start to the span.</param>
/// <param name="length">The length of the span.</param>
/// <returns>An array of variables which conjunction will be false if the
/// sub-list is assigned to True, and correctly bounded by variables assigned
/// to False, or by the start or end of works.</returns>
static ILiteral[] NegatedBoundedSpan(BoolVar[] works, int start, int length)
{
var sequence = new List<ILiteral>();
if (start > 0)
sequence.Add(works[start - 1]);
foreach (var i in Range(length))
sequence.Add(works[start + i].Not());
if (start + length < works.Length)
sequence.Add(works[start + length]);
return sequence.ToArray();
}
/// <summary>
/// C# equivalent of Python range (start, stop)
/// </summary>
/// <param name="start">The inclusive start.</param>
/// <param name="stop">The exclusive stop.</param>
/// <returns>A sequence of integers.</returns>
static IEnumerable<int> Range(int start, int stop)
{
foreach (var i in Enumerable.Range(start, stop - start))
yield return i;
}
/// <summary>
/// C# equivalent of Python range (stop)
/// </summary>
/// <param name="stop">The exclusive stop.</param>
/// <returns>A sequence of integers.</returns>
static IEnumerable<int> Range(int stop)
{
return Range(0, stop);
}
}
我试着按照这个ShiftSchedulingSat指南
但是如果添加我的约束,会不断得到不可行的结果
有什么建议吗?可以用另一种方式建模吗?