具有多个列表的排列

时间:2011-05-09 19:55:05

标签: c# .net permutation

我需要将工人安排到特定班次的工作岗位上。工作人员为他们希望工作的工作设置了一个优先顺序。工作时间最少的工人获得第一个工作偏好。只有一名工人可以从事某项工作。

我试图通过创建锯齿状数组来解决这个问题。阵列按最低工时的工人排序。每个子数组都是特定的工作者首选项列表。数组中的数字是作业代码。然后我生成了排列,直到我得到一个有效的解决方案。

例如,下面列出了28名工人以及每个工人的工作偏好。

int[][] preference = {
    new int[] {1,2,3,4,5,6,7,8,11,12,13,14,15},
    new int[] {2,4,5,6,7,8,11,12,13,14,15},
    new int[] {1,3,6,7,8,9,10,14,15,18,19,22,23,24,26,27,29,30,32,34,35,25,36},
    new int[] {4,5,12,13},
    new int[] {9,10,11,14,15,1,2,6},
    new int[] {9,10,11,14,2,6,18,19,27,29,30,31,32,35},
    new int[] {11,12,13,14,2,4,5,6},
    new int[] {12,13,4,5},
    new int[] {9,10,11,13,14,15,1,2,6,7,8,18,19,21,22,23,24,26,27,28,29,30,31,32,34,35,16,17,33,36},
    new int[] {1,2,9,10,11,14,15,18,19,30,31,32,35,37,33},
    new int[] {4,13,18,19,35},
    new int[] {18,19,35},
    new int[] {21,22,23,24,18,19},
    new int[] {22,23,24,25,18,19,16,17},
    new int[] {18,19,23,24,35},
    new int[] {18,19,23,24,35},
    new int[] {27,26,28,29,30,32,34,35,36},
    new int[] {27,26,30,32,34,35,36},
    new int[] {28,29,30,31,32,33,35},
    new int[] {28,29,30,31,32,33,26,35,36},
    new int[] {26,29,30,31,32,34,35,36},
    new int[] {28,29,31,32,33,26,35,36},
    new int[] {27,28,29,30,31,32,35,33,1,2,3,9,10,11,14,18,19,6,15},
    new int[] {34,35,36,26,27,31,32},
    new int[] {31,32,34,35,2,11,14,18,19,23,24,6,15,16,17,20},
    new int[] {37,29,30,31,32,35,33,36,2,9,10,11,14,18,19,23,24,6,15,16,17},
    new int[] {18,19,35},
    new int[] {18,19,35},
};

preference[0]包含第一个工人首选项列表。 prererence[1]包含第二个工作者引用列表,依此类推。在此示例中,第一个工作人员已选择处理作业1,2,3,4,5,6,7,8,11,12,13,14,15。 正确的解决方案是:1,2,3,4,9,10,11,12,14,15,13,18,21,22,23,24,27,26,28,29,30,31,32,34,6,37,19,35

我遇到的问题是表现。我尝试使用递归和非递归循环迭代,性能很糟糕。我的想法是必须有一个不同的方法来解决问题或一个已经做到这一点我可以购买的图书馆。提前感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

当你有

  1. 你的工人按照“谁先选谁”排序
  2. 首选项按降序排序和
  3. 排序
  4. 每个工人应该只交一次作业
  5. 然后我认为应该可以使用两个游标和已经分配的作业列表在O(n2 log(n))中执行作业分配算法。

    是否有其他解决方案优化要求,您没有说明?:例如。

    1. 你需要找到一个解决方案,在这个解决方案中,尽可能少的工人被分配了他们根本没有偏好的工作。或
    2. 所有已分配作业的首选等级之和应该是数学上最低的。
    3. 如果是这样,算法会更复杂。

      如果通过有效的解决方案你的意思是所有工作人员从他们的偏好列表中找到工作的任何工作分配,那么我会质疑你的算法的适用性作为现实世界轮班工作的答案分配问题。你经常会在没有解决方案的情况下结束。

答案 1 :(得分:1)

假设严格遵守资历/优先级(我知道我公司的每小时工作岗位是严格的资历),你可以简化:

var availableJobs = new List<int>(... jobs here ...);
var usedJobs = HashSet<int>();
var selectedJobs = new Dictionary<int,int>(); // keyed by employee #

for (int ww = 0; ww < preference.Count(); ++ww)
{
    // this will throw an exception if strict ordering is unsolvable
    try
    {
        // Remove the try...catch if job cannot be 0 and use FirstOrDefault
        int job = preference[ww].First(jj => !usedJobs.Contains(jj));
        selectedJobs.Add(ww, job);
        usedJobs.Add(job);
    }
    catch(InvalidOperationException ioe)
    {
        Console.WriteLine("Cannot allocate job code to worker: {0}", ww);
        Console.WriteLine("No job preference matches.");
        Console.WriteLine("    Job codes avaliable");
        Console.WriteLine("    -------------------");
        foreach (var jobCode in availableJobs.Except(usedJobs))
        {
             Console.WriteLine("{0}", jobCode);
        }

        break;
    }
}

if (selectedJobs.Count() == preference.Count())
{
    // jobs have been assigned per worker
}

现在,如果您没有严格的遵守政策,那么您正在考虑优化问题,问题是您是否允许更高优先级的个人获得较低的偏好职位代码?

大多数优化算法与轮班工作者可能没有相同的“公平”概念。

答案 2 :(得分:0)

如果您在分配给作业时从列表中删除该员工,则后续作业选择将更快。另一种看待它的方法是沿着员工列表走一次,对于每个员工,走下他们的工作偏好并将他们分配到他们的偏好列表中的第一个打开的工作。这样你只需要看一次每个员工。