Алгоритм справедливого распределения заданий для работников на основе навыков - PullRequest
14 голосов
/ 22 января 2011

(Прежде чем кто-нибудь спросит, это не домашняя работа.)

У меня есть набор работников с интересами, т.е.

  • Боб: Java, XML, Ruby

  • Сьюзен: Java, HTML, Python

  • Фред: Python, Ruby

  • Сэм: Java, Ruby

  • и т.д.

(на самом деле где-то в пределах 10-25 "интересов" на каждого работника у меня около 40-50 работников)

В то же время у меня есть очень большой набор задач, которые необходимо распределить среди рабочих. Каждое задание должно быть назначено как минимум 3 работникам, и работники должны соответствовать по крайней мере одному из интересов задания:

Задача 1: Ruby, XML Задача 2: XHTML, Python

и так далее. Так что Боб, Фред или Сэм могут получить задание 1; Сьюзен или Фред могут получить задание 2.

Все это хранится в базе данных, таким образом:

Task
    id integer primary key
    name varchar

TaskInterests
    task_id integer
    interest_id integer

Workers
    id integer primary key
    name varchar
    max_assignments integer

WorkerInterests
    worker_id
    interest_id

Assignments
    task_id
    worker_id
    date_assigned

У каждого работника есть максимальное количество назначений, которое он будет выполнять, около 10. Некоторые интересы встречаются реже, чем другие (т. Е. Только 1 или 2 работника указали их как интересы), некоторые интересы встречаются чаще (т.е. половина рабочие перечисляют их).

Алгоритм должен :

  • Назначьте каждое задание 3 работникам (это Предполагается, что по крайней мере 3 из рабочие заинтересованы в одном из интересы задачи).
  • Назначьте каждому работнику 1 или более задач

В идеале алгоритм будет:

  • Назначьте каждому работнику количество задач, пропорциональное их максимальным назначениям и общему количеству задач. Например, если Сьюзен говорит, что она выполнит 20 заданий, а большинство людей выполнят только 10 заданий, а 50 работников и 300 заданий, ей следует назначить 12 заданий (20/10 * (300/50)).
  • Назначьте различные задачи каждому работнику, поэтому, если Сьюзен перечислит 4 интереса, она получит задачи, которые включают 4 интереса (вместо того, чтобы получить 10 задач с одинаковыми интересами)

Наиболее сложным аспектом на сегодняшний день является решение этих вопросов:

  • задачи, имеющие интересы с несколькими соответствующими работниками
  • рабочих, у которых мало интересов, особенно
  • рабочих, которые имеют несколько интересов, для которых есть относительно немного задач

Ответы [ 4 ]

6 голосов
/ 23 января 2011

Эта проблема может быть смоделирована как Проблема максимального расхода .

В задаче с максимальным потоком у вас есть ориентированный граф с двумя особыми узлами - источником и приемником. Ребра в графе имеют емкости, и ваша цель состоит в том, чтобы назначить поток через граф из источника в сток, не превышая ни одного из мощностей ребер.

С (очень) тщательно составленным графиком мы можем найти задание, соответствующее вашим требованиям, из максимального потока.

Позвольте мне перечислить требования.

Требуется:

1. Workers are assigned no more than their maximum assignments.
2. Tasks can only be assigned to workers that match one of the task's interests.
3. Every task must be assigned to 3 workers.
4. Every worker must be assigned to at least 1 task.

Дополнительно:

5. Each worker should be assigned a number of tasks proportional to that worker's maximum assignments
6. Each worker should be assigned a variety of tasks.

Я предполагаю, что максимальный поток найден с помощью Алгоритм Эдмондса-Карпа .

Давайте сначала найдем график, который соответствует требованиям 1-3.

Изобразите график в виде 4 столбцов узлов, где ребра проходят только от узлов в столбце до узлов в соседнем столбце справа.

В первом столбце у нас есть узел источника. В следующем столбце у нас будут узлы для каждого из работников. Исходя из этого, каждый работник имеет преимущество, равное максимальным назначениям этого работника. Это обеспечит выполнение требования 1.

В третьем столбце есть узел для каждой задачи. От каждого работника во втором столбце есть преимущество для каждой задачи, в которой этот работник заинтересован с емкостью 1 (работник интересуется задачей, если пересечение их интересов непусто). Это приведет к выполнению требования 2. При пропускной способности 1 каждый работник будет использовать только 1 из 3 слотов для каждой задачи.

В четвертой колонке у нас есть раковина. От каждой задачи есть грань до уровня приемника 3. Это обеспечит выполнение требования 3.

Теперь мы находим максимальный поток на этом графике, используя алгоритм Эдмондса-Карпа. Если этот максимальный поток меньше, чем 3 * (# of tasks), тогда нет требований по выполнению задания 1-3. Если нет, то есть такое назначение, и мы можем найти его, изучив окончательный расширенный график. На расширенном графике, если есть преимущество от задачи до работника с емкостью 1, то этот работник назначается этой задаче.


Теперь мы изменим наш график и алгоритм для соответствия остальным требованиям.

Сначала давайте выполним требование 4. Это потребует небольшого изменения алгоритма. Изначально установите все мощности от источника до рабочих на 1. Найдите максимальный поток на этом графике. Если поток не равен количеству рабочих, то требования соответствия назначению 4 не существует. Теперь в вашем окончательном остаточном графике для каждого рабочего ребро от источника к этому работнику имеет емкость 0, а обратное ребро имеет емкость 1 Измените их на that worker's maximum assignments - 1 и 0, соответственно. Теперь продолжайте алгоритм Эдмондса-Карпа, как и раньше. По сути, мы сначала нашли такое назначение, чтобы каждому работнику было назначено ровно одно задание. Затем удалите обратный край из этой задачи, чтобы рабочий всегда был назначен хотя бы для одной задачи (хотя это может быть не та задача, которая была назначена в первом проходе).


Теперь давайте выполним требование 5. Строго говоря, это требование просто означает, что мы делим максимальные назначения каждого работника на sum of all worker's maximum assignments / number of tasks. Это вполне может не иметь удовлетворительного назначения. Но это нормально. Инициализируйте наш график с этими новыми максимальными назначениями. Запустите Эдмондс-Карп. Если он находит поток, который насыщает края от задач к погружению, мы закончили. В противном случае мы можем увеличить пропускную способность от приемника до рабочих на остаточном графике и продолжить работу Edmonds-Karp. Повторяйте, пока мы не насыщаем края в раковину. Не увеличивайте мощности настолько сильно, чтобы работнику было назначено слишком много задач. Кроме того, технически приращение для каждого работника должно быть пропорционально максимальным назначениям этого работника. И то, и другое легко сделать.


Наконец, давайте выполним требование 6. Это немного сложно. Сначала добавьте столбец между рабочими и задачами и удалите все ребра из рабочих задач. В этом новом столбце для каждого работника добавьте узел для каждого из этих рабочих интересов. Из каждого из этих новых узлов добавьте ребро к каждой задаче с совпадающим интересом с емкостью 1. Добавьте ребро от каждого работника к каждому из его узлов интереса с емкостью 1. Теперь поток в этом графе обеспечит это, если работник назначается n задач, то пересечение объединения интересов этих задач с интересами этого работника имеет размер не менее n. Снова, возможно, что есть удовлетворяющее назначение без этого назначения, но нет ни одного с ним. Мы можем справиться с этим так же, как и с требованием 5: запустить Edmonds-Karp до завершения, если не выполняется удовлетворительное назначение, увеличить пропускную способность от рабочих к их интересующим узлам и повторить.

Обратите внимание, что в этом измененном графике мы больше не удовлетворяем требованию 3, поскольку один работник может быть назначен нескольким / всем слотам задачи, если пересечение их интересов имеет размер больше 1. Мы можем это исправить. Добавьте новый столбец узлов между узлами интереса и узлами задачи и удалите ребра между этими узлами. Для каждого сотрудника в новом столбце вставьте узел для каждой задачи (чтобы у каждого сотрудника был свой узел для каждой задачи). От этих новых узлов к соответствующей задаче справа добавьте ребро с емкостью 1. От узла интересов каждого работника к узлам задач этого работника добавьте ребро с емкостью 1 от каждого интереса к каждой соответствующей задаче.

-

РЕДАКТИРОВАТЬ: Позвольте мне попытаться объяснить это немного. Пусть -(n)-> будет ребром с n-емкостью.

Ранее у нас было worker-(1)->task для каждой пары работник-задача с совпадающим интересом. Теперь у нас есть worker-(k)->local interest-(1)->local task-(1)->global task. Теперь вы можете думать о задаче, которая соответствует паре рабочий-интерес. Первое преимущество говорит о том, что для работника каждый из его интересов может быть сопоставлен с k задачами. Второе преимущество говорит о том, что интересы каждого работника могут быть сопоставлены только один раз с каждой работой. Третье ребро говорит о том, что каждое задание может быть назначено только один раз каждому работнику. Обратите внимание, что вы можете передать несколько потоков от рабочего к локальной задаче (равных размеру пересечения их интересов), но только 1 поток от рабочего к глобальному узлу задачи из-за третьего ребра.

-

Также обратите внимание, что мы не можем правильно смешать это приращение с одним для требования 5 правильно. Однако мы можем запустить весь алгоритм по одному разу для каждой емкости {1,2, ..., r} для рабочих -> интересов. Затем нам нужен способ ранжировать назначения. То есть, если мы ослабим требование 5, мы сможем лучше выполнить требование 6 и наоборот. Однако есть другой подход, который я предпочитаю для смягчения этих ограничений.


Лучший подход к ослаблению требований (по мотивам / по шаблону templatetypedef)

Когда мы хотим иметь возможность ослабить множество требований (например, 5 и 6), мы можем смоделировать это как проблему максимального расхода с минимальными затратами. Это может быть проще, чем инкрементальный поиск, который я описал выше.

Например, для требования 5 установите все ребра затрат на 0. У нас есть начальное ребро от источника к работнику с мощностью, равной worker's maximum assignments / (sum of all worker's maximum assignments / number of tasks) и со стоимостью 0. Затем вы можете добавить еще одно ребро с помощью оставшаяся емкость для этого работника и стоимость 1. Другая возможность может заключаться в использовании некоторой прогрессивной стоимости, так что при добавлении задач к работнику увеличивается стоимость добавления другой задачи для этого пользователя. Например. вместо этого вы могли бы разделить оставшиеся рабочие места работника на отдельные ребра с затратами 1,2,3,4,....

Подобное можно было бы затем сделать между рабочими узлами и узлами локального интереса для требования 6. Взвешивание должно быть сбалансировано, чтобы отразить относительную важность различных требований.

Этот метод также достаточен для обеспечения выполнения требования 4. Кроме того, затраты для требования 5, вероятно, должны быть сделаны такими, чтобы они были пропорциональны максимальным назначениям работника. Тогда назначение 1 дополнительного задания работнику с максимум 100 не будет стоить столько же, сколько назначение дополнительного работника с максимумом 2.


Анализ сложности

Пусть n = # of employees, m = # of tasks, k = max interests for a single task/worker, l = # of interests, j = maximum of maximum assignments.

Требование 3 подразумевает, что n = O (m). Давайте также предположим, что l = O(m) и j = O(m).

В меньшем графе (до изменения по требованию 6) граф имеет n + m + 2 = O(m) вершин и самое большее n + m + k*min(n, m) = O(km) ребер.

После изменения у него есть 2 + n + n * l + n * m + m = O(nm) вершин и n + k * n + k * m * n + n * m + m = O(kmn) ребер (технически нам может потребоваться j * n + j * l больше узлов и ребер, чтобы не было нескольких ребер от одного узла к другому, но это не изменит асимптотику связанно). Также обратите внимание, что нет необходимости в ребре> j.

Используя минимальную формулировку максимального потока, мы можем найти решение в O(VEBlogV), где V = # vertices, E = # edges и B = max capacity on a single edge. В нашем случае это дает O(kjn^2m^2log(nm)).

3 голосов
/ 22 января 2011

Для задач, в которых трудно найти прямое решение, было бы неплохо использовать алгоритм аппроксимации, функцию эваляции и метод для улучшения решения.Существуют различные подходы, такие как генетические алгоритмы и имитация отжига .

Основная идея состоит в том, чтобы использовать какой-то простой алгоритм (такой как жадный алгоритм) получить что-то неопределенно пригодное для использования и производить случайные модификации, сохраняя те изменения, которые улучшают оценку, и отбрасывая те, которые ухудшают ее.

С помощью генетических алгоритмов генерируется и оценивается группа, например, из 100 случайных решенийа лучшие сохраняются и «разводятся» для получения решений нового поколения с характеристиками, аналогичными характеристикам предыдущих поколений, но с некоторыми случайными мутациями.

При моделируемом отжиге вероятность принятия несколько худших решений высокаизначально, но уменьшается со временем.Это снижает риск застрять на местном оптимиуме на ранней стадии.

1 голос
/ 23 января 2011

Попробуйте сопоставить свою задачу с проблемой стабильного брака . Задачи становятся будущими женами, а ваши сотрудники становятся женихами.

Возможно, вы захотите добавить некоторый дополнительный алгоритм для назначения предпочтений каждой задачи сотрудникам, и наоборот - вы можете назначить некоторый идеальный необходимый навык для компонентов каждой задачи, а затем позволить своим сотрудникам ранжировать каждую задачу. Вы можете назначить навык для каждого компонента, которым обладает каждый сотрудник, и использовать его, чтобы получить предпочтение каждой задачи среди сотрудников.

Как только у вас появятся предпочтения, запустите алгоритм, опубликуйте результаты, а затем разрешите людям подавать заявки на вас в парах, чтобы поменять местами задания - после всего этого проблема с людьми, и люди работают лучше, когда имеют степень управление.

1 голос
/ 23 января 2011

Итак, я немного подумал над этой проблемой и думаю, что вы можете получить хорошее решение (для некоторого определения «хорошо»), сократив его до экземпляра минимального потока с минимальной стоимостью (см. this , например).Идея заключается в следующем.Предположим, в качестве входных данных вам дан набор заданий J, каждый из которых обладает необходимым набором навыков, а также набором рабочих W, каждый из которых обладает набором талантов.Вам также дается для каждого работника константа k_i, указывающая, сколько заданий вы хотели бы сделать, а также константа m_i, обозначающая максимальное количество заданий, которые вы можете выделить им.Ваша цель - назначить рабочие места таким образом, чтобы каждая работа выполнялась работником, обладающим навыками, ни один работник не выполняет больше, чем m_i работ, а количество «лишних» работ, выполняемых работниками, сводится к минимуму.,Например, если пять работников хотят выполнить четыре задачи, и нагрузка сбалансирована так, что два работника выполняют четыре работы, один выполняет три, а другой - пять, общее превышение равно одному, поскольку один работник сделал еще одноРабота, чем ожидалось.

Сокращение заключается в следующем.Сейчас мы проигнорируем требование балансировки и просто посмотрим, как уменьшить его до максимального потока;мы добавим балансировку нагрузки в конце.Построить граф G с назначенным начальным узлом s и узлом приемника t.Добавьте к этому графику узел для каждого задания j и каждого работника w.Будет ребро от s до каждого из этих j узлов с нулевой стоимостью и емкостью один.Также будет ребро от каждого w узла до t с нулевой стоимостью и емкостью m_i.Наконец, для каждого задания j и работника w, если у работника w есть таланты, необходимые для выполнения задания j, существует переход от j к w с нулевой стоимостью и емкостью один.

Идея состоит в том, что мы хотимпротолкнуть поток от s к t через узлы j и w так, что каждый путь потока, проходящий через некоторый узел j к узлу w, означает, что задание j следует передать работнику w.Ограничение емкости на ребрах от узлов s до j гарантирует, что не более одной единицы потока входит в узел j, поэтому задание назначается не более одного раза.Ограничение емкости на ребрах от w узлов до узла t не позволяет назначать каждому рабочему слишком много раз.Поскольку все мощности являются интегральными, существует интегральный максимальный поток от s до t, и поэтому максимальный поток на этом графике соответствует законным назначениям рабочих мест и не превышает максимальную нагрузку любого работника.Вы можете проверить, все ли задания назначены, посмотрев на общий поток на графике;если оно равно количеству заданий, все они назначены.

Однако приведенная выше конструкция ничего не делает для уравновешивания рабочих нагрузок.Чтобы это исправить, мы немного изменим конструкцию.Вместо того, чтобы иметь ребро от каждого w-узла к t, вместо этого для каждого w-узла добавьте два графа к графу c и e и соедините их следующим образом.Существует ребро от w_i до c_i с емкостью k_i и нулевой стоимостью, и идентичное ребро от c_i до t.Существует также преимущество от w_i до e_i со стоимостью 1 и емкостью m_i - k_i.Существует также грань от e_i до t с равной емкостью и нулевой стоимостью.

Интуитивно мы не изменили объем потока, который покидает какой-либо узел w, но мы изменили, сколько стоит этот поток.Поток, перенаправленный на t через узел c, свободен, и поэтому работник может выполнять задания k_i без затрат.Любые задания после этого должны направляться через e, что стоит по одной на каждую единицу потока, пересекающего его.Нахождение максимального потока в этом новом графике все еще определяет назначение, но нахождение максимального потока минимальной стоимости на графике находит назначение, которое минимизирует избыточные задания, разделенные на рабочих.

Максимальные потоки минимальной стоимостиможно решить за полиномиальное время с помощью нескольких хорошо известных алгоритмов, так что, надеюсь, это полезный ответ!

...