Общая информация:
Что такое проблема ранца?
"Проблема ранца - это проблема комбинаторной оптимизации: с учетом набора элементов, каждый с весом и значением определяет количество каждого предмета, включаемого в коллекцию, чтобы общий вес был меньше или равен заданному пределу, а общее значение было как можно большим ». Википедия.
В частности, проблема с ранцем 0/1 не позволяет брать дроби предметов.
Вот пример решения этой проблемы с помощью Dynami c Программирование: https://youtu.be/cJ21moQpofY
Пример:
Из видео выше - у нас есть набор предметов:
- Значения: 2, 2, 4, 5, 3
- Вес: 3, 1, 3, 4, 2
- Макс. Вес: 7
После решения наш стол будет выглядеть так:
Из этого мы можем сказать, что максимальное значение равно 10, и мы выбрали пункты 2, 4 и 5
Мой вопрос:
Используя PHP, я написал рекурсивную функцию, используя памятку, которая правильно решает проблему KS, возвращает оптимальное решение и возвращает элементы, выбранные в этом оптимальном решении. То есть программа создает приведенную выше таблицу, чтобы найти решение, а затем работает в обратном направлении, чтобы найти элементы. Можно ли также найти дополнительные решения из той же таблицы? Скажем, я хочу найти 3 лучших решения?
Что я пробовал:
- Сначала я попытался использовать массив в рекурсивной функции для отслеживания из выбора. Проблема: если вы сбросите массив в конце ветви, вы потеряете предыдущие варианты root. поскольку функция рекурсивная, она может вызываться где-то из середины ветви.
- Одна мысль состоит в том, чтобы отслеживать рекурсивную глубину путем добавления переменной глубины к вызову функции. Сложение и вычитание из него по мере необходимости. Таким образом, возможно, я мог бы построить 2d (или 3d?) Массив дерева, показывающий каждый пройденный путь. Это кажется довольно тяжелым в пространстве?
- Моя текущая цель состоит в том, чтобы я мог как-то использовать таблицу, которая у меня уже есть, как в примере, и работать в обратном направлении из этого набора данных, чтобы найти результаты, меньшие, чем оптимальные. По сути, из приведенного выше примера я бы начал со строки 6, строки 5 (9), чтобы посмотреть, что произойдет, если я уменьшу емкость? или строка 7, строка 4 (9), чтобы увидеть, что произойдет, если я потеряю предмет? затем сравните результаты. Будет ли это на самом деле работать?
Обновление: ОК, я попробовал программирование в варианте 3 там. Короткий ответ, это не работает. Если бы у нас была полная таблица, мы могли бы работать в обратном направлении, чтобы получить другие результаты, но код довольно уродливый, и я не уверен, что он действительно более эффективен, чем разрешение для меньших элементов / весов. Кроме того, таблица из таблицы memo (массива) не полная, что означает, что мы действительно можем вернуть некоторые альтернативы, но они не являются исчерпывающими. Наш массив в виде таблицы выглядит примерно так:
Обновление: Ответ на самом деле заключается в том, что вам нужно перезапустить функцию с различными ограничениями веса / элемента. Если вы подумаете об этом, любое неоптимальное решение не будет использовать весь KS или выберет меньшее количество элементов, поэтому просто решите его, уменьшив переменные ограничения ($ n, $ c). Вы можете отслеживать результаты вызовов функций, пока не найдете N-й лучший результат. Если вы хотите, скажем, первые 3 результата, это не проблема, но это может стать слишком интенсивным, если вам нужно отслеживать каждый результат из большого набора данных.
Мой код
Это * Код 1074 *, который в настоящее время решает KS с помощью динамического программирования c, используя запоминание и возвращает элементы, но только с оптимального результата. Позже я правильно запишу его в класс.
<code><?php
$value = array(2, 2, 4, 5, 3);
$weight = array(3, 1, 3, 4, 2);
$maxWeight = 7;
$maxItems = 5;
$seen = array(array()); //2D array for memoization
$picked = array();
//Put a dummy zero at the front to make things easier later.
array_unshift($value, 0);
array_unshift($weight, 0);
//Call our Knapsack Solver and return the sum value of optimal set
$KSResult = KSTest($maxItems, $maxWeight, $value, $weight);
$maxValue = $KSResult; //copy the result so we can recreate the table
//Recreate the decision table from our memo array to determine what items were picked
//Here I am building the table backwards because I know the optimal value will be at the end
for($i=$maxItems; $i > 0; $i--) {
for($j=$maxWeight; $j > 0; $j--) {
if($seen[$i][$j] != $seen[$i-1][$j]
&& $maxValue == $seen[$i][$j]) {
array_push($picked, $i);
$maxValue -= $value[$i];
break;
}
}
}
//Print out picked items and max value
print("<pre>".print_r($picked,true)."
"); echo $ KSResult; // Рекурсивная формула для решения проблемы KS // $ n = количество проверяемых элементов // $ c = общая вместимость сумки function KSTest ($ n, $ c, & $ value, & $ weight) {global $ seen; if (isset ($ seen [$ n] [$ c])) {// Мы видели эту подзадачу до возврата $ seen [$ n] [$ c]; } if ($ n === 0 || $ c === 0) {// Больше нет элементов для проверки или больше нет емкости $ result = 0; } elseif ($ weight [$ n]> $ c) {// Этот элемент слишком тяжелый, проверьте следующий элемент без этого $ result = KSTest ($ n-1, $ c, $ value, $ weight ); } else {// Получите более высокий результат сохранения или не сохранения элемента $ tempVal1 = KSTest ($ n-1, $ c, $ value, $ weight); $ tempVal2 = $ value [$ n] + KSTest ($ n-1, $ c - $ weight [$ n], $ value, $ weight); if ($ tempVal2> = $ tempVal1) {$ result = $ tempVal2; // некоторые условия могут go здесь? в противном случае используйте max ()} else {$ result = $ tempVal1; }} // запоминаем результаты и возвращаем $ seen [$ n] [$ c] = $ result; вернуть $ результат; }?>