Рюкзак с непрерывным (не внятным) ограничением - PullRequest
8 голосов
/ 14 января 2012

Я смотрел Динамическое программирование - проблема с рюкзаком (YouTube) .Тем не менее, я решаю немного другую проблему, где ограничением является бюджет, цена, в два раза, а не целое число.Так что мне интересно, как я могу изменить это?Двойной является "непрерывным" в отличие от целого числа, где я могу иметь 1,2,3 .... Я не думаю, что я делаю 0,0, 0,1, 0,2 ...?

ОБНОВЛЕНИЕ 1

Я думал о преобразовании double в int умножением на 100. Деньги - только 2 десятичных знака.Но это будет означать, что диапазон значений будет очень большим?

ОБНОВЛЕНИЕ 2

Проблема, которую мне нужно решить:

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

В видео на YouTube автор создал два двухмерных массива, например int [numItems] [возможный объем (вес)].Здесь я не могу, так как бюджет это двойное число, а не целое число

Ответы [ 7 ]

17 голосов
/ 17 января 2012

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

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

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

5 голосов
/ 21 января 2012

Вам придется сделать то, что вы сказали в ОБНОВЛЕНИИ 1: выразить бюджет и цены на товары в центах (при условии, что мы говорим о долларах).Тогда мы не говорим о произвольной точности или непрерывных числах.Каждая цена (и бюджет) будет целым числом, просто это целое будет представлять центы.

Чтобы упростить ситуацию, давайте предположим, что бюджет составляет 10 долларов.Проблема заключается в том, что емкость рюкзака должна принимать все значения в:

[0.00, 0.01, 0.02, 0.03, ..., 9.99, 10.00] 

Значения два много.Каждая строка МАТРИЦЕ РЕШЕНИЯ и МАТРИЦЫ KEEP будет иметь 1001 столбец, поэтому вы не сможете решить проблему вручную (если бюджет составляет миллионы долларов, даже компьютеру может быть трудно)но это присуще исходной проблеме (вы ничего не можете с этим поделать).

Ваша лучшая ставка - это использовать какой-то существующий код для KNAPSACK или, возможно, написать свой (я нене советую).

Если вы не можете найти существующий код для KNAPSACK и знакомы с Linux / Mac, я предлагаю вам установить GNU Linear Programming Kit (GLPK) и выразитьпроблема как целочисленная линейная программа или бинарная линейная программа (если вы пытаетесь решить рюкзак 0-1).Это решит проблему для вас (плюс вы можете использовать ее через C, C ++, Python и, возможно, Java, если вам нужно).Для получения справки по использованию GLPK проверьте эту замечательную статью (вам, вероятно, понадобится part 2 , где говорится о целочисленных переменных).Если вам нужна дополнительная помощь по GLPK, пожалуйста, оставьте комментарий.

РЕДАКТИРОВАТЬ:

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

Не пугайтесь, потому что ваш бюджет может составлять несколько долларов -> несколько сотен центов.Если ваш бюджет составляет всего 18 центов, размер вашей проблемы будет сопоставим с размером в видео на YouTube.Парень из видео не смог бы решить свою проблему (вручную), если бы его размер рюкзака был 1800 (или даже 180).

3 голосов
/ 17 января 2012

Это не ответ на ваш вопрос, но также может быть то, что вы ищете:

Линейное программирование

Я использовал Microsoft Solver Foundation 3 для создания простого кода, который решает описанную вами проблему. Он не использует алгоритм ранца , а симплексный метод .

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SolverFoundation.Common;
using Microsoft.SolverFoundation.Services;

namespace LPOptimizer
{
    class Item
    {
        public String ItemName { get; set; }
        public double Price { get; set; }
        public double Satisfaction { get; set; }

        static void Main(string[] args)
        {
            //Our data, budget and items with respective satisfaction and price values
            double budget = 100.00;
            List<Item> items = new List<Item>()
            {
                new Item(){
                    ItemName="Product_1", 
                    Price=20.1, 
                    Satisfaction=2.01
                },
                new Item(){
                    ItemName="Product_2", 
                    Price=1.4, 
                    Satisfaction=0.14
                },
                new Item(){
                    ItemName="Product_3", 
                    Price=22.1, 
                    Satisfaction=2.21
                }
            };

            //variables for solving the problem.
            SolverContext context = SolverContext.GetContext();
            Model model = context.CreateModel();
            Term goal = 0; 
            Term constraint = 0; 

            foreach (Item i in items)
            {
                Decision decision = new Decision(Domain.IntegerNonnegative, i.ItemName);
                model.AddDecision(decision); //each item is a decision - should the algorithm increase this item or not?

                goal += i.Satisfaction * decision; //decision will contain quantity.
                constraint += i.Price * decision; 
            }

            constraint = constraint <= budget; //this now says: "item_1_price * item_1_quantity + ... + item_n_price * item_n_quantity <= budget";

            model.AddConstraints("Budget", constraint); 

            model.AddGoals("Satisfaction", GoalKind.Maximize, goal); //goal says: "Maximize: item_1_satisfaction * item_1_quantity + ... + item_n_satisfaction * item_n_quantity"

            Solution solution = context.Solve(new SimplexDirective());
            Report report = solution.GetReport();
            Console.Write("{0}", report);
            Console.ReadLine();
        }
    }
}

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

Из кода очевидно, что у вас может быть количество предметов в реальных значениях (двойное). Это, вероятно, также будет быстрее, чем рюкзак с большим диапазоном (если вы решите использовать * 100, который вы упомянули).

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

Редактировать

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

Edit2

Согласно вашему Обновлению 2 , я обновил этот код, чтобы включить удовлетворение.

2 голосов
/ 16 ноября 2014

Ответы не совсем правильные.

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

Сначала стандартное решениепроблема с целочисленными призами:

Define K[0..M, 0..n] where K[j, i] = optimal value of items in knapsack of size j, using only items 1, ..., i

for j = 0 to M do K[j,0] = 0
for i = 1 to n do
    for j = 0 to M do
        //Default case: Do not take item i
        K[j,1] = K[j, i-1]
        if j >= w_i and v_i+K[j-w, i-1] > K[j, i] then
            //Take item i
            K[j,i] = v_i + K[j-w_i, i-1]

Это создает таблицу, в которой решение можно найти, следуя рекурсии для записи K [M, n].

Теперь решение для проблемы свес действительного числа:

Define L[0..S, 0..N] where L[j, i] = minimal weight of items in knapsack of total value >= j, using only items 1, ..., i
and S = total value of all items

for j = 0 to S do L[j, 0] = 0
for i = 0 to n do
    for j = 0 to S do
        //Default case: Do not take item i
        L[j,i] = L[j, i-1]
        if j >= v_i and L[j-v_i, i-1] + w_i < L[j, i] then
            //Take item i
            L[j, i] = L[j -v_i, i-1] + w_i

Решение теперь можно найти аналогично другой версии.Вместо того, чтобы использовать вес в качестве первого измерения, мы теперь используем общую стоимость предметов, которые приводят к минимальному весу.Код имеет более или менее одинаковое время выполнения O (S * N), тогда как другой имеет O (M * N).

2 голосов
/ 24 января 2012

Вопрос, недавно опубликованный в sci.op-research, предложил мне долгожданную передышку от некоторой утомительной работы, о которой я предпочел бы не думать, и о которой вы бы не хотели слышать.Мы знаем, что жадная эвристика решает задачу непрерывного ранца максимизировать c'xs.ta′x≤bx≤ux∈ℜ + n (1) до оптимальности.(Доказательство с использованием теории двойственности довольно просто.) Предположим, что мы добавим то, что я назову ограничением подсчета, получая maximizec′xs.ta′x≤be′x = b˜x≤ux∈ℜ + n (2) где е = (1,…, 1).Может ли это быть решено с помощью чего-то другого, кроме симплекс-метода, такого как вариант жадной эвристики?

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

Метод, который я представлю, основан наЧто касается двойственности, в частности, хорошо известный результат, что если выполнимое решение линейной программы и выполнимое решение ее дуала удовлетворяют условию дополнительной слабости, то оба являются оптимальными в своих соответствующих задачах.Обозначим двойственные переменные для ограничений по ранцу и счету λ и µ соответственно.Заметим, что λ≥0, но µ неограничен по знаку.По сути, тот же метод, изложенный ниже, будет работать с ограничением числа неравенств (e′x≤b˜), и на самом деле будет немного проще, поскольку мы априори знаем знак µ (неотрицательный).В постере исходного вопроса было указано ограничение на равенство, поэтому я буду этим пользоваться.Существуют также двойственные переменные (ρ≥0) для верхних оценок.Двойственная проблема: минимализмbλ + b˜μ + u′ρs.t.λa + μe + ρ≥cλ, ρ≥0. (3)

Это пост в блоге, а не диссертация, япредположим, что (2) выполнимо, что все параметры строго положительны, а оптимальное решение единственно и не вырождено.Уникальность и вырожденность не приведут к аннулированию алгоритма, но они могут усложнить представление.В оптимальном базовом выполнимом решении (2) будет одна или две базовые переменные - одна, если ограничение на рюкзак не является обязательной, две, если она является обязательной, - с любой другой переменной, не являющейся основной, в нижней или верхней границе.Предположим, что (λ, µ, ρ) является оптимальным решением двойственности (2).Приведенная стоимость любой переменной xi равна ri = ci − λai − µ.Если ограничение по ранцу не является обязательным, то λ = 0 и оптимальным решением является xi = uiri> 0b˜ − ∑rj> 0ujri = 00ri <0. (4) Если ограничение по ранцу является обязательным, будет два элемента (j,k) чьи переменные являются основными, с rj = rk = 0.(Предполагая отсутствие вырожденности, я исключил возможность того, что переменная слабины в ограничении ранца является базовой со значением 0).Положим xi = uiri> 00ri <0 (5) и пусть b ′ = b − ∑i∉ {j, k} aixi и b˜ ′ = b˜ − ∑i∉ {j, k} xi.Две основные переменные задаются как xj = b′ − akb˜′aj − akxk = b′ − ajb˜′ak − aj. (6) </p>

Алгоритм будет проходить в два этапа, сначала ищарешение с несвязанным ранцем (одна базовая переменная x) и затем поиск решения с привязкой ранца (две базовые переменные x).Заметьте, что в первый раз, когда мы найдем выполнимые первичные и двойственные решения, подчиняющиеся дополнительному расслаблению, оба должны быть оптимальными, поэтому мы закончилиТакже отметим, что для любого µ и любого λ≥0 мы можем завершить его, чтобы получить выполнимое решение (3), задав ρi = ci − λai − µ +.Таким образом, мы всегда будем иметь дело с выполнимым двойным решением, и алгоритм будет создавать первичные решения, которые удовлетворяют дополнительному ослаблению.Следовательно, критерий остановки сводится к тому, чтобы построенное первичное решение было выполнимым.

Для первой фазы мы сортируем переменные так, чтобы c1≥ ⋯ ≥cn.Поскольку λ = 0 и существует одна базовая переменная (xh), стоимость которой сниженаt ноль, очевидно, µ = ch. Это означает, что приведенная стоимость ri = ci − λai − μ = ci − ch для xi неотрицательна для ih. Если решение (3) выполнимо, то есть, если ∑ih. Таким образом, мы можем использовать поиск пополам, чтобы завершить этот этап. Если мы примем большое значение n, начальная сортировка может быть выполнена за время O (nlogn), а оставшаяся часть фазы требует O (logn) итераций, каждая из которых использует время O (n).

К сожалению, я не вижу способа применить поиск деления пополам ко второй фазе, в которой мы ищем решения, в которых ограничение по ранцу является обязательным и λ> 0. Мы снова будем искать значение μ, но на этот раз монотонно. Сначала примените жадную эвристику к задаче (1), сохраняя ограничение ранца, но игнорируя ограничение счета. Если решение происходит случайно, чтобы удовлетворить ограничение количества, мы закончили. Однако в большинстве случаев ограничение количества будет нарушено. Если счет превышает b˜, то можно сделать вывод, что оптимальное значение µ в (4) положительно; если отсчет не достигает b˜, оптимальное значение µ является отрицательным. Мы начинаем вторую фазу с μ = 0 и движемся в направлении оптимального значения.

Так как жадный эвристик сортирует элементы так, что c1 / a1≥ ⋯ ≥cn / an, и поскольку мы начинаем с μ = 0, наш текущий порядок сортировки имеет (c1 − μ) / a1≥ ⋯ ≥ (cn − μ ) / ан. Мы сохраним этот порядок (прибегая по мере необходимости) при поиске оптимального значения μ. Чтобы избежать путаницы (я надеюсь), позвольте мне предположить, что оптимальное значение μ положительно, так что мы будем увеличивать μ по мере продвижения. Мы ищем значения (λ, µ), где две из переменных x являются основными, что означает, что две имеют уменьшенную стоимость 0. Предположим, что это происходит для xi и xj; затем п = 0 = rj⟹ci-λai-μ = 0 = Cj-λaj-μ (7) ⟹ci-μai = λ = Cj-μaj. Легко показать (оставлено читателю в качестве упражнения), что если (c1 − μ) / a1≥ ⋯ ≥ (cn − μ) / an для текущего значения μ, то следующее более высокое (более низкое) значение μ который создает связь в (7), должен включать в себя последовательную пару последовательных элементов (j = i + 1). Более того, снова отказываясь от вырождения (в данном случае это означает, что более двух элементов с уменьшенной стоимостью 0), если мы слегка подтолкнем μ к значению, при котором элементы i и i + 1 снизили стоимость 0, единственное изменение порядка сортировки будет что пункты i и i + 1 меняются местами. Дальнейшее движение в этом направлении не приведет к тому, что i и i + 1 снова начнут связываться, но, конечно, любой из них может в конечном итоге связать своего нового соседа по дороге.

Второй этап, начиная с μ = 0, происходит следующим образом. Для каждой пары (i, i + 1) вычисляют значение μi μ, при котором (ci − μ) / ai = (ci + 1 − μ) / ai + 1; замените это значение на ∞, если оно меньше текущего значения μ (что указывает на то, что связь происходит в неправильном направлении). Обновите µ до miniμi, вычислите λ из (7) и вычислите x из (5) и (6). Если x является выполнимым (в основном, 0≤xi≤ui и 0≤xi + 1≤ui + 1), stop: x является оптимальным. В противном случае поменяйте местами i и i + 1 в порядке сортировки, установите μi = ∞ (переиндексированные элементы i и i + 1 больше не будут связываться) и пересчитайте μi-1 и μi + 1 (обмен не влияет на другие μj).

Если первая фаза не нашла оптимума (и если жадной эвристике в начале второй фазы не повезло), вторая фаза должна завершиться с оптимумом, прежде чем она исчерпает значения μ для проверки ( все μj = ∞). Вырождение может быть обработано либо с небольшими дополнительными усилиями при кодировании (например, проверка нескольких комбинаций i и j во второй фазе, когда происходят трехсторонние или более высокие связи), либо с помощью небольших возмущений, чтобы сломать вырождение.

2 голосов
/ 17 января 2012

Вы посмотрели на это .Извините, у меня нет права комментировать.

Изменить 1

Вы говорите, что ограничение - это бюджет вместо ранца вес?Это по-прежнему остается проблемой ранца.

Или вместо Значения предметов как целых чисел (проблема с ранцем 0-1) у вас есть дробей .Тогда с жадным подходом все будет в порядке.

Edit 2

Если я правильно понимаю вашу проблему .. В ней говорится

У нас есть n видовпункты, от 1 до n.Каждый вид предмета i имеет значение vi и цену pi.Обычно мы предполагаем, что все значения и цены неотрицательны.Бюджет составляет B.

. Наиболее распространенная формулировка проблемы - 0-1 knapsack problem, которая ограничивает число xi копий каждого вида предмета нулем или единицей.Математически задача о ранце 0-1 может быть сформулирована следующим образом:

         n
maximize E(vi.xi)
         i=i

           n
subject to E(pi.xi) <= B,         xi is a subset of {0,1}
           i=1

Ответ Нео Адониса находится здесь. Динамическое программирование не будет работать с произвольной точностьюна практике.

Но если вы хотите ограничить точность, скажем, до 2 десятичных знаков ..., продолжайте, как объяснено в видео ... ваша таблица должна выглядеть примерно так ..

+------+--------+--------+--------+--------+--------+--------+
| Vi,Pi| 0.00   | 0.01   | 0.02   | 0.03   | 0.04 ...    B   |
+------+--------+--------+--------+--------+--------+--------+
|4,0.23|        |        |        |        |        |        |
|2,2.93|        |        |        |        |        |        |
|7,9.11|        |        |        |        |        |        |
| ...  |        |        |        |        |        |        |
| Vn,Pn|        |        |        |        |        | answer |
+------+--------+--------+--------+--------+--------+--------+

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

Да, диапазон значений очень большой, и вы также должны понимать, что рюкзак равен NP-complete, т. Е. Не существует эффективного алгоритмачтобы решить это.только pseudo polynomial решение с использованием DP.см это и это .

1 голос
/ 21 января 2012

Ответ на ваш вопрос зависит от нескольких факторов:

  1. Насколько велико значение ограничения (если оно масштабируется до лант и конвертируется в целые числа).
  2. Сколько элементовтам.
  3. Какую проблему с рюкзаком нужно решить
  4. Какая требуемая точность.

Если у вас очень большое значение ограничения (гораздо большечем миллионы) и очень много предметов (гораздо больше, чем тысячи)

Тогда единственный вариант - Алгоритм жадного приближения .Сортируйте элементы в порядке убывания значения на единицу веса и упакуйте их в этом порядке.

Если вы хотите использовать простой алгоритм и не требовать высокой точности

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

Если у вас очень большое (или даже непрерывное) значение ограничения, но довольно небольшое числоэлементы (менее тысячи)

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

Если значение ограничения и количество элементов невелики

Используйте любойподход (DP, ответвление и ограничение или просто перебор).

Если значение ограничения довольно мало (меньше миллионов), но слишком много (например, миллионов) элементов

Тогда возможны алгоритмы DP.

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

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

...