Google 或工具根据轮班工作所需的员工人数进行员工排班

问题描述 投票:0回答:0

我有关于在谷歌或工具中安排的问题。

我正在尝试根据班次需要多少员工来构建调度程序,所以白天我需要 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指南

但是如果添加我的约束,会不断得到不可行的结果

有什么建议吗?可以用另一种方式建模吗?

c# scheduling or-tools cp-sat-solver cp-sat
© www.soinside.com 2019 - 2024. All rights reserved.