Реализация комбинаторной оптимизации

Мне поручили решить задачу, которая, на первый взгляд, кажется чем-то вроде обобщенной задачи присваивания (подмножество задач комбинаторной оптимизации: http://en.wikipedia.org/wiki/Generalized_assignment_problem).

Описание проблемы довольно непонятное и написано не на английском языке; пожалуйста, потерпите меня, поскольку я пытаюсь объяснить это как можно лучше:

Мне дается набор «разрезов» (заданий), которые нужно выполнить на больших мраморных блоках определенных размеров. Цель состоит в том, чтобы выполнить все задачи в заданном списке, сводя к минимуму количество потерянного мрамора. Если у меня есть, например, начальный блок размера 20, то я могу разрезать его по-разному, чтобы выполнить определенное количество задач, при этом следя за тем, чтобы не «потерять» мрамор в процессе. Если мне нужны подблоки из 3, 5, 6 и 6, то блок начального размера 20 вполне подойдет, так как у меня не будет потерянных шариков (3+5+6+6 = 20).

Таким образом, цель состоит в том, чтобы выполнить все задачи, потеряв при этом как можно меньше мрамора. Я могу использовать любое количество стартовых блоков, если все задачи выполнены. Одно дополнительное ограничение заключается в том, что каждой задаче, определяющей подблок длины L, также назначается «класс», который представляет, какая конкретная машина требуется для выполнения этого конкретного разреза. Имея начальный блок, я не могу использовать более 3 разных классов, чтобы разрезать свой блок. Я могу сделать столько разрезов, сколько захочу, на одной машине, если я не меняю класс более двух раз.

Вот пример данных, которые могут быть предоставлены мне:

2 20 38
7
20
4 1
7 1
3 1
25 1
22 2
22 1
20 2
17 1
10 1
22 2
27 2
26 2
15 3
13 4
12 5
15 6
27 4
27 5
27 7
27 2

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

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

В третьей строке указано общее количество разрезов (задач), необходимых для выполнения операции. В этом случае для решения задачи необходимо выполнить ровно 20 заданий.

Остальные строки дают необходимый разрез, а также класс, связанный с этой задачей.

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

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

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

Спасибо!

PS: Алгоритм будет написан на Java, если это поможет.


person user3211102    schedule 27.11.2014    source источник
comment
Подход, который вы выберете, действительно зависит от того, насколько точное решение вам нужно, как быстро оно должно выполняться и сколько времени вы хотите посвятить кодированию. Если вам нужно точное оптимальное решение и вы хотите реализовать довольно сложный алгоритм, используйте подход ILP/генерации столбцов, о котором упоминает @j_random_hacker. Если вам нужно что-то быстрое и грязное, и вы не возражаете против некоторой неточности, напишите жадную эвристику. Если вам нужно что-то среднее, вам, вероятно, потребуется найти несколько журнальных статей с хорошей эвристикой.   -  person LarrySnyder610    schedule 28.11.2014
comment
Да, я остановился на жадной эвристике, поскольку мне нужно было что-то, что можно было бы выполнить относительно быстро.   -  person user3211102    schedule 29.11.2014


Ответы (1)


Если я правильно понимаю, то (игнорируя ограничения «класса») это проблема резки заготовки. Это трудная задача оптимизации NP, которую обычно решают, формулируя ее как ILP, а затем применяя особый вид метода решателя ILP, который называется генерация столбца. Вкратце, в ILP есть переменная для каждого возможного шаблона или способа разрезания мраморного блока на максимальное количество подблоков, которая записывает количество блоков, которые должны быть вырезаны по этому конкретному шаблону. Но может быть огромное количество возможных шаблонов, большинство из которых вообще не будут использоваться в оптимальном решении; Генерация столбцов позволяет решателю ILP работать с меньшим набором переменных, который гарантированно содержит все ненулевые (т. е. все фактически используемые шаблоны).

На странице Википедии есть много полезной информации. Особый интерес представляет тот факт, что если имеется только один начальный размер блока, то минимизация потерь аналогична минимизации количества используемых мастер-блоков, и тогда проблема эквивалентна Упаковка в корзину. Эта задача все еще NP-сложная, но она одна из «хороших»: есть простые эвристики, которые (доказуемо) очень близки к оптимальному решению. Возможно, вы сможете адаптировать это в эвристику для вашей проблемы.

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

person j_random_hacker    schedule 28.11.2014