exponenta event banner

Назначение офиса двоичным целочисленным программированием: на основе проблем

В этом примере показано, как решить задачу назначения с помощью двоичного целочисленного программирования с использованием подхода к задаче оптимизации. Для получения информации о подходе на основе решателей см. раздел Назначения Office по двоичному целочисленному программированию: на основе решателей.

Проблема назначения Office

Вы хотите назначить шесть человек, Марсело, Ракеш, Питер, Том, Марджори и Мэри Энн, в семь офисов. В каждом офисе может быть не более одного человека, а каждый человек получает ровно один кабинет. Так что будет один пустой офис. Люди могут давать преференции офисам, а их предпочтения рассматриваются исходя из их стажа. Чем дольше они работали в MathWorks, тем выше их стаж. Некоторые офисы имеют окна, некоторые - нет, а одно окно меньше других. Кроме того, Питер и Том часто работают вместе, поэтому должны быть в соседних офисах. Марсело и Ракеш часто работают вместе, и должны находиться в соседних офисах.

Компоновка офиса

Офисы 1, 2, 3 и 4 находятся внутри офисов (без окон). Офисы 5, 6 и 7 имеют окна, но окно в офисе 5 меньше, чем два других. Вот как устроены офисы.

officelist = {'Office 1','Office 2','Office 3','Office 4','Office 5','Office 6','Office 7'};
printofficeassign(officelist)

Постановка проблемы

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

namelist = {'Mary Ann','Marjorie','Tom','Peter','Marcelo','Rakesh'};

Создание двоичных переменных, индексированных по номеру и имени офиса.

occupy = optimvar('occupy',namelist,officelist,...
    'Type','integer','LowerBound',0,'Upperbound',1);

Старшинство

Вы хотите взвесить предпочтения в зависимости от стажа, чтобы чем дольше вы работаете в MathWorks, тем больше ваших предпочтений. Старшинство выглядит следующим образом: Мэри Энн 9 лет, Марджори 10 лет, Том 5 лет, Питер 3 года, Марсело 1,5 года, и Ракеш 2 года. Создайте нормированный весовой вектор на основе стажа.

seniority = [9 10 5 3 1.5 2];
weightvector = seniority/sum(seniority);

Предпочтения Народного кабинета

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

MaryAnn = [0, 0, 0, 0, 10, 40, 50];
Marjorie = [0, 0, 0, 0, 20, 40, 40];
Tom = [0, 0, 0, 0, 30, 40, 30];
Peter = [1, 3, 3, 3, 10, 40, 40];
Marcelo = [3, 4, 1, 2, 10, 40, 40];
Rakesh = [10, 10, 10, 10, 20, 20, 20];

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

prefmatrix = [MaryAnn;Marjorie;Tom;Peter;Marcelo;Rakesh];

Весить матрицу предпочтений на weightvector для масштабирования столбцов по старшинству.

PM = diag(weightvector) * prefmatrix;

Целевая функция

Цель заключается в максимальном удовлетворении преференций, взвешенных по стажу. Это линейная целевая функция sum(sum(occupy.*PM)).

Создайте задачу оптимизации и включите целевую функцию.

peopleprob = optimproblem('ObjectiveSense','maximize','Objective',sum(sum(occupy.*PM)));

Ограничения

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

peopleprob.Constraints.constr1 = sum(occupy,2) == 1;

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

peopleprob.Constraints.constr2 = sum(occupy,1) <= 1;

Вы хотите, чтобы Том и Питер не больше одного офиса друг от друга, и то же самое с Марсело и Ракешем.

Установите ограничения, что Том и Питер не более чем на 1 расстояние друг от друга.

peopleprob.Constraints.constrpt1 = occupy('Tom','Office 1') + sum(occupy('Peter',:)) - occupy('Peter','Office 2') <= 1;
peopleprob.Constraints.constrpt2 = occupy('Tom','Office 2') + sum(occupy('Peter',:)) - occupy('Peter','Office 1') ...
    - occupy('Peter','Office 3') - occupy('Peter','Office 5') <= 1;
peopleprob.Constraints.constrpt3 = occupy('Tom','Office 3') + sum(occupy('Peter',:)) - occupy('Peter','Office 2') ...
    - occupy('Peter','Office 4') - occupy('Peter','Office 6') <= 1;
peopleprob.Constraints.constrpt4 = occupy('Tom','Office 4') + sum(occupy('Peter',:)) - occupy('Peter','Office 3') ...
    - occupy('Peter','Office 7') <= 1;
peopleprob.Constraints.constrpt5 = occupy('Tom','Office 5') + sum(occupy('Peter',:)) - occupy('Peter','Office 2') ...
    - occupy('Peter','Office 6') <= 1;
peopleprob.Constraints.constrpt6 = occupy('Tom','Office 6') + sum(occupy('Peter',:)) - occupy('Peter','Office 3') ...
    - occupy('Peter','Office 5') - occupy('Peter','Office 7') <= 1;
peopleprob.Constraints.constrpt7 = occupy('Tom','Office 7') + sum(occupy('Peter',:)) - occupy('Peter','Office 4') ...
    - occupy('Peter','Office 6') <= 1;

Теперь создайте ограничения, которые Марсело и Ракеш не более чем на 1 расстояние друг от друга.

peopleprob.Constraints.constmr1 = occupy('Marcelo','Office 1') + sum(occupy('Rakesh',:)) - occupy('Rakesh','Office 2') <= 1;
peopleprob.Constraints.constmr2 = occupy('Marcelo','Office 2') + sum(occupy('Rakesh',:)) - occupy('Rakesh','Office 1') ...
    - occupy('Rakesh','Office 3') - occupy('Rakesh','Office 5') <= 1;
peopleprob.Constraints.constmr3 = occupy('Marcelo','Office 3') + sum(occupy('Rakesh',:)) - occupy('Rakesh','Office 2') ...
    - occupy('Rakesh','Office 4') - occupy('Rakesh','Office 6') <= 1;
peopleprob.Constraints.constmr4 = occupy('Marcelo','Office 4') + sum(occupy('Rakesh',:)) - occupy('Rakesh','Office 3') ...
    - occupy('Rakesh','Office 7') <= 1;
peopleprob.Constraints.constmr5 = occupy('Marcelo','Office 5') + sum(occupy('Rakesh',:)) - occupy('Rakesh','Office 2') ...
    - occupy('Rakesh','Office 6') <= 1;
peopleprob.Constraints.constmr6 = occupy('Marcelo','Office 6') + sum(occupy('Rakesh',:)) - occupy('Rakesh','Office 3') ...
    - occupy('Rakesh','Office 5') - occupy('Rakesh','Office 7') <= 1;
peopleprob.Constraints.constmr7 = occupy('Marcelo','Office 7') + sum(occupy('Rakesh',:)) - occupy('Rakesh','Office 4') ...
    - occupy('Rakesh','Office 6') <= 1;

Решить проблему назначения

Звонить solve для решения проблемы.

[soln,fval,exitflag,output] = solve(peopleprob);
LP:                Optimal objective value is -33.836066.                                           


Optimal solution found.

Intlinprog stopped at the root node because the objective value is within a gap tolerance of the optimal value,
options.AbsoluteGapTolerance = 0 (the default value). The intcon variables are integer within tolerance,
options.IntegerTolerance = 1e-05 (the default value).

Посмотрите решение - кто получил каждый офис?

numOffices = length(officelist);
office = cell(numOffices,1);
for i=1:numOffices
    office{i} = find(soln.occupy(:,i)); % people index in office
end

whoinoffice = officelist; % allocate
for i=1:numOffices
    if isempty(office{i})
        whoinoffice{i} = ' empty  ';
    else
        whoinoffice{i} = namelist(office{i});
    end
end

printofficeassign(whoinoffice);
title('Solution of the Office Assignment Problem');

Качество решения

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

fval,exitflag,output
fval = 33.8361
exitflag = 1
output = struct with fields:
        relativegap: 0
        absolutegap: 0
      numfeaspoints: 1
           numnodes: 0
    constrviolation: 0
            message: 'Optimal solution found.↵↵Intlinprog stopped at the root node because the objective value is within a gap tolerance of the optimal value, options.AbsoluteGapTolerance = 0 (the default value). The intcon variables are integer within tolerance, options.IntegerTolerance = 1e-05 (the default value).'
             solver: 'intlinprog'

Связанные темы