Генерирайте само валидни конфигурации с ограничения

Току-що започнах да работя по проект, който включва известно оптимизиране на графика, и се притеснявам, че съм в математическите води над главата си. Чудех се дали можете да измислите някакъв хитър начин да направите следното.

Ето основните неща:

  • Имате x брой времеви интервали
  • Имате y брой интервюиращи от факултета
  • Имате z брой кандидати, които интервюират
  • x и y не трябва да са равни (може да има различен брой интервюиращи и интервюирани)
  • Възможно е за времеви интервал никой да не бъде интервюиран.
  • Те съставят таблица като график, където заглавията на редовете са интервюиращите (представени с числа), заглавията на колоните са времевите интервали (представени с числа), а самите клетки са кандидатите, които интервюират.

Ограничения:

  • Цифрата z може да се появи само веднъж във всеки ред/колона като судоку (тъй като кандидатът не може да интервюира два пъти с един и същ интервюиращ и той/тя не може да интервюира два пъти наведнъж).
  • Трябва да има точно 3 от всяко z число в цялата таблица (защото всички кандидати трябва да интервюират точно 3 пъти).

В крайна сметка ще се използва някаква формула за изчисляване на претеглен резултат за всяка валидна конфигурация, за да се определи „най-доброто“ състояние на дъската. За обяснение просто ще кажем, че това е нещо като 2a + 5b, където a и b са различни видове качества, които интервюиращите имат общи с кандидатите, но това не е важно за сега.

Опитвах се да измисля някакъв начин за генериране на всички възможни валидни конфигурации и в крайна сметка да изчисля резултата на всяка валидна конфигурация, като накрая избрах тази с най-висок резултат. Сблъсках се с някои проблеми при намирането на умен начин да го направя.

Първоначално не се чувствах умен и си помислих за грубо форсиране, но това е принципно невъзможно поради голямото разнообразие от възможности.

Можете ли да измислите някакъв по-хитър начин за мен да отрежа някои от грешните конфигурации или хитър начин просто да генерирам валидни табла за проверка на претеглени резултати?


person user2911578    schedule 23.10.2013    source източник
comment
IMO, ако искате в крайна сметка да изберете комбинацията с най-добрия претеглен резултат, тогава трябва да формулирате проблема като целочислен проблем с линейно програмиране, да намерите средство за решаване и да го разрешите.   -  person Abhishek Bansal    schedule 23.10.2013
comment
Бих разгледал вашия проблем като случай на Проблем с училищното разписание. Разгледахте ли го?   -  person Julien    schedule 23.10.2013
comment
Ето видео за внедряване на проблем с училищно разписание с отворен код (все пак в java:/ )   -  person Geoffrey De Smet    schedule 23.10.2013


Отговори (2)


Ето един доста прост модел за програмиране на ограничения в MiniZinc, който - при правилния решаващ инструмент - генерира всички възможни валидни решения за (моята интерпретация на) проблема. Въпреки това, както посочва @TimChippingtonDerrick, за по-сложни проблемни случаи има твърде много решения.

Въпреки това, ако целта (obj = "2a + 5b") може по някакъв начин да бъде заявена в модела (напр. като се използват някои допълнителни данни на интервюиращия/кандидатите и т.н.), това е просто въпрос на промяната й на оптимизационен проблем, т.е. минимизиране на или максимизиране на тази цел.

И така, ето модела MiniZinc за прост екземпляр на проблем (x=10, y=5, z=10 и m=3). Забележка: в таблицата с интервюта стойността 0 (нула) означава, че няма интервю за интервюиращия по това време.

include "globals.mzn"; 

int: x = 10; % number of time slots
int: y =  5; % number of faculty interviewers
int: z = 10; % number of applicants interviewing

int: m = 3; % number of times each applicants will be interviewed

% decision variables
% The interview table:
%   1..y: rows (interviewers)
%   1..x: columns (time slots)
array[1..y, 1..x] of var 0..z: s;

% solve satisfy;
solve :: int_search([s[i,j] | i in 1..y, j in 1..x], first_fail, indomain_random, complete) satisfy;

constraint

    % A z digit can only appear once in each row/column like Sudoku (because an applicant 
    % can't interview with the same interviewer twice and he/she can't interview twice at once).
    forall(a in 1..z) (
       % rows
       forall(i in 1..y) (
          %sum([bool2int(s[i,j] = a) | j in 1..x]) <= 1
          alldifferent_except_0([bool2int(s[i,j] = a) | j in 1..x])
       )
       /\
       % columns
       forall(j in 1..x) (
          % sum([bool2int(s[i,j] = a) | i in 1..y]) <= 1
          alldifferent_except_0([bool2int(s[i,j] = a) | i in 1..y])
       )
    )

    % There has to be exactly 3 of each z number in the entire table 
    % (because all applicants have to interview exactly 3 times).
    /\
    forall(a in 1..z) (
       count([s[i,j] | i in 1..y, j in 1..x], a, m)
    )
;

% constraint
%   % symmetry breaking: assign applicant a to interviewer a in time a (if possible)
%   forall(a in 1..z where a <= y) (
%      s[a,a] = a
%   )
% ;


output [
   "x (number of time slots (rows): " ++ show(x) ++ "\n" ++
   "y (number of interviews (columns): " ++ show(y) ++ "\n" ++
   "z (number of applicants): " ++ show(z) ++ "\n"
]
++
[
  if j = 1 then "\n" else " " endif ++
    show_int(2,s[i,j])
  | i in 1..y, j in 1..x
];

За този проблем (x=10, y=5, z=10) има огромен брой решения. Ето две от тях:

x (number of time slots (rows): 10
y (number of interviews (columns): 5
z (number of applicants): 10

5  2 10  8  0  6  7  4  3  9
3  1  5  7  0  2  9  6  8  4
8  0  0  5  6  1 10  3  0  7
2 10  1  0  0  9  0  0  0  0
0  0  0  0  0  4  0  0  0  0
----------
x (number of time slots (rows): 10
y (number of interviews (columns): 5
z (number of applicants): 10

 5  2 10  8  0  6  7  4  3  9
 3  1  5  7  0  2  9  6  8  4
 8  0  0  5  6  1 10  3  0  7
 2 10  0  1  0  9  0  0  0  0
 0  0  0  0  0  4  0  0  0  0

(Този модел MiniZinc също е тук: http://www.hakank.org/minizinc/interview_scheduling.mzn )

Числен коментар за по-прост проблем (x=6,y=3,z=3, m=3), за да се покаже множеството на проблема: Има 317760 различни решения, когато не се използва нарушаването на симетрията (което е коментирано в код). С нарушаването на симетрията има много по-малко решения: 1300. Ето едно от тези решения:

1  0  0  3  2  0
3  2  1  0  0  0
0  1  3  2  0  0
person hakank    schedule 26.10.2013

Неговият вид е неизложен в отговорите по-горе, вероятно защото е очевиден за някои от нас, но просто опитът да се изброят всички легални комбинации ще работи само в много малки тестови случаи на този вид проблем. Бързо ще ударите стената във времето за изчисление, ако опитате наивна реализация.

Както беше предложено от други, ще трябва да разгледате използването на някои инструменти за оптимизация, ако искате да направите това за проблеми в реален мащаб. Или един от безплатните решаващи програми като COIN-OR, glpk или lpsolve; или някой от търговските инструменти - те могат да станат скъпи, но ако имате достатъчно голям проблем, тогава те лесно си заслужават цената.

person TimChippingtonDerrick    schedule 24.10.2013