工人调度算法

时间:2010-09-23 05:45:37

标签: algorithm graph constraints scheduling matching

问题

这是我想要解决的问题的本质。在周末,我们有工人在托儿所照顾孩子。一个周末有16个不同的插槽可供填写。因此,对于为期4周的月份,有64个插槽可供填充。我们有最多30名托儿所工人(虽然我们需要更多。像孩子一样的人吗?)。

编辑:每个时段都是离散的 - 它们不重叠。

目前,每个月都有人提出托儿所时间表。每个月根据每个人的喜好制定这个时间表是一项复杂而耗时的任务。在考虑了我自己想到的问题后,“必须有更好的方法!”

算法

我注意到问题基本上是bipartite graphmarriage problem也是一个二分图,您可以使用匹配算法Edmonds's matching algorithm来解决。

但是这假设一个节点集中的每个节点只匹配另一个节点集中的一个节点。就我而言,每个托儿所工作者只能工作一个时间段。由于我们严重缺乏人手,这是行不通的!一群人每个月必须工作两次才能填满所有的时间段。

这似乎意味着这更像是经典的“医院/居民问题”。它与婚姻问题的不同之处在于,“女性”可以接受来自不止一个“男人”的“建议”(例如,医院可以接纳多个居民)。就我而言,一个托儿所工人可以占用一个以上的时间段。

现在怎么办?

呼!

现在我已经完成了设置....有没有人知道任何解释或显示这种算法的好链接?有没有更好的方法来解决这个问题?我在想它吗?我做了一个谷歌搜索“医院居民算法”,并找到了研究生的论文。尔加!我毕业于CS学位并参加了AI课程......但那是6年前的事了。 帮助!

Aaaaany建议表示赞赏!!

2 个答案:

答案 0 :(得分:4)

“医院/居民问题”确实可行,但这取决于你的限制因素:

  • 医院拥有最大容量,并将订购居民(最想要的不太想要)。
  • 居民将订购医院。
  • 没有其他可能的限制。

在您的情况下,医院是工人,居民是老虎机。

  • 老虎机可以命令工人(也许你早上更喜欢试验过的......)。
  • 工人可以订购插槽。
  • 但你不能有其他的限制,例如:“我早上工作,我不想在晚上工作。”

如果你没问题,那么你有可能:

  • 你想有利于工人:“以医院为导向的案例”。

    您将尝试将工作人员分配到他们的首选插槽。

  • 你想利用插槽:“驻留导向案例”

    每个插槽都有他们喜欢的工作人员。

我去年必须编码,这是代码。

/* 
RO : needed for Resident-Oriented version
HO : needed for Hospital-Oriented version
*/
const int MAX_R = 1000;
const int MAX_H = 1000;
const int INF = 1000*1000*1000;

您需要填写输入变量。 一切都很简单:

  • R_pref和H_pref是居民/医院的偏好列表
  • H_rank [h] [r]是H_pref [h]中r的等级:r在h的首选项列表中的位置

就是这样。

// Input data
int R, H;                   // Number of Residents/Hospitals
int C[MAX_H];               // Capacity of hospitals
vector<int> R_pref[MAX_R], H_pref[MAX_H]; // Preferences : adjency lists
/*RO*/int H_rank[MAX_H][MAX_R];   // Rank : rank of r in H_pref[h]
/*HO*/int R_rank[MAX_R][MAX_H];   // Rank : rank of h in R_pref[r]

无需触摸下方。

// Internal data
int RankWorst[MAX_H];   // Rank of the worst r taken by h
/*RO*/int BestH[MAX_R];       // Indice of the best h in R_pref the r can get
/*HO*/int BestR[MAX_H];       // Indice of the best r in H_pref the h can get
int Size[MAX_H];        // Number of residents taken by h

// Output data
int M[MAX_R];

void stable_hospitals_RO()
{
    for(int h = 0 ; h < H ; h++)
      RankWorst[h] = H_pref[h].size()-1;
    fill_n(BestH, R, 0);
    fill_n(Size, H,0);
    fill_n(M,R,INF);
    for (int i = 0; i < R; i++)
        for (int r = i; r >= 0;)
        {
        if(BestH[r] == int(R_pref[r].size()))
            break;
            const int h = R_pref[r][BestH[r]++];
            if(Size[h]++ < C[h])
            {
                M[r] = h;
                break;
            }
            int WorstR = H_pref[h][RankWorst[h]];
            while(WorstR == INF || M[WorstR] != h) // Compute the worst
                WorstR = H_pref[h][--RankWorst[h]];
            if(H_rank[h][r] < RankWorst[h])        // Ranked better that worst
            {
                M[r] = h;
                M[r = WorstR] = INF;    // We have eliminate it, he need to put it somewhere
            }
        }
}
void stable_hospitals_HO()
{
    fill_n(BestR, H, 0);
    fill_n(Size, H,0);
    fill_n(M,R,INF);
    vector<int> SH;
    for (int h = 0; h < H; h++)
        SH.push_back(h);
    while(!SH.empty())
    {
        int h = SH.back();
        if(Size[h] == C[h] || BestR[h] == int(H_pref[h].size())) // Full or no r available
        {
            SH.pop_back();
            break;
        }
    const int r = H_pref[h][BestR[h]++];
    // r is unassigned or prefer h to current hospital
        if(M[r] == INF || R_rank[r][h] < R_rank[r][M[r]]) 
        {
            if(++Size[h] == C[h]) // Will be full
                SH.pop_back();
            if(M[r] != INF) // Delete from M[r]
            {
                Size[M[r]]--;
                SH.push_back(M[r]);
            }
            M[r] = h;
        }
    }
}

用于显示如何从prefs构建排名的示例。 (在这种情况下,首选项列表在stdin上)。

int main()
{
    scanf("%d%d",&R,&H);
    int num;
    // put inf

    for(int r = 0 ; r < R ; r++)
    {
        scanf("%d",&num);
        R_pref[r].resize(num);
        for(int h = 0 ; h < num ; h++)
        {
            scanf("%d",&R_pref[r][h]);
            R_rank[r][R_pref[r][h]] = h;
        }
    }
    for(int h = 0 ; h < H ; h++)
    {
        scanf("%d",&C[h]);
        scanf("%d",&num);
        H_pref[h].resize(num);
        for(int r = 0 ; r < num ; r++)
        {
            scanf("%d",&H_pref[h][r]);
            H_rank[h][H_pref[h][r]] = r;
        }
    } 
    stable_hospitals_RO();
    printf("\n\n\n\n");
    stable_hospitals_HO();
    return 0;
}

举个例子: 医院1至3,6个résidents。

H_pref:

  • 1 - &gt; 2 5 6 1(首选2然后5然后6然后1)
  • 2 - &gt; 4 2 1 6 3 5
  • 3 - &gt; 1 2

R_pref:

  • 1 - &gt; 1 2 3
  • 2 - &gt; 3 1
  • 3 - &gt; 2 1
  • 4 - &gt; 1 3 2
  • 5 - &gt; 3 2 1
  • 6 - &gt; 3

H_rank:

  • 1 - &gt; 4 1 INF INF 2 3(1在H_pref [1]中的位置4,3不是theree)
  • 2 - &gt; 3 2 5 1 6 4
  • 3 - &gt; 1 2 INF INF INF INF

类似于R_rank。

医院不必对每个人进行排名,也可以排名人数少于他们的能力。

答案 1 :(得分:1)

如果有人遇到类似的问题,这就是我选择的解决方案:

我最终使用Backtracking Search Algorithm来约束满意度问题。它只是进行正常的回溯搜索,但在遍历树时检查是否满足所有约束。这是伪代码:

function BACKTRACKING-SEARCH(csp) returns a solution, or failure
     return BACKTRACK({ }, csp)

function BACKTRACK(assignment, csp) returns a solution, or failure
     if assignment is complete then return assignment
     var = SELECT-UNASSIGNED-VARIABLE(csp)
     for each value in ORDER-DOMAIN-VALUES(var, assignment, csp) do
        if value is consistent with assignment then
           add {var = value} to assignment
           result = BACKTRACK(assignment, csp)
           if result != failure then
              return result
           remove {var = value} 
     return failure

变量是一个时间段。分配给变量的可能值是worker。变量及其实际赋值的组合是树中的节点。因此,搜索空间是时隙和工作者的所有可能组合。约束修剪来自搜索空间的节点。

约束可以是工作人员可用性。因此,如果时隙A被分配给工作者X,但是X不能在时隙A中工作,则该节点将被判定为不一致。

我通过将每个时隙/工作者组合视为其OWN时隙,解决了为多个工作人员分配特定时隙的问题。因此,如果孩子的托儿所有2名工人要填补,我认为这是两个单独的时间段来填补,每个时间段都分配了自己的工人。这样每个变量只分配一个值。它使得更简单的算法成为可能。

感谢所有帮助,将其归结为可解决的问题。