алгоритм, чтобы найти лучшую комбинацию - PullRequest
12 голосов
/ 25 марта 2009

Предположим, у меня есть список из 100 товаров, каждый из которых имеет свою цену. Каждый из них также имеет измерение энергии (кДж).

Можно ли найти наилучшую комбинацию из 15 продуктов менее чем за 10 долларов, из которых сумма энергии (кДж) была наибольшей при использовании программирования?

Я знаю C #, но любой язык в порядке. Приветствия.

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

Ответы [ 10 ]

14 голосов
/ 25 марта 2009

http://en.wikipedia.org/wiki/Knapsack_problem

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

6 голосов
/ 25 марта 2009

Это больше похоже на линейное программирование задачу.

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

Проверьте Симплексный метод .

4 голосов
/ 25 марта 2009

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

Требуются переменные includeItem1, ..., includeItemN с ограничениями 0 ≤ includeItem i ≤ 1 для всех значений i и includeItem1 + ... + includeItemN ≤ 15, и includeItem1 * priceItem1 + ... ≤ 10, максимизируя includeItem1 * kilojouleItem1 + ....

Вставьте это в ваш любимый целочисленный линейный программный решатель и получите решение:)

См. Также http://en.wikipedia.org/wiki/Linear_programming

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

Я не думаю, что ваша проблема - это особый случай ILP, который облегчает ее решение. Рассматривая это как проблему, похожую на рюкзак, вы можете ограничиться просмотром всех подмножеств 1..100, которые содержат не более (или точно) 15 элементов, что является полиномом от n --- это n-choose-15 что меньше, чем (n ^ 15) / (15!), но это не очень полезно, когда n = 100.

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

1 голос
/ 27 марта 2009

Да, как все отмечали, это сложная проблема с рюкзаком. Что-то такое простое может быть достаточно хорошим, хотя ...

SELECT TOP 15 *
FROM Product
WHERE Price < 10
ORDER BY Energy DESC
1 голос
/ 25 марта 2009

Перечень репозитория алгоритма Stony Brook реализации для задачи о ранце.

Их книга Руководство по разработке алгоритмов содержит информацию такого рода для широкого спектра проблем.

1 голос
/ 25 марта 2009

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

Заказывайте товары по соотношению энергии / цены, выбирая 100% самых высоких, пока у вас не кончатся деньги, затем выберите дробное значение самого высокого оставшегося.

Например, если цены 4,3,5,4, а энергии 3,5,2,7, заказ составляет

7/4, 5/3, 3/4, 2/5

Таким образом, вы выбрали бы предметы 4 и 2, которые стоили бы 7 $, а за оставшиеся 3 $ вы купили бы 75% первого предмета по цене 3 $ и энергии 3 * .75 = 2.25

Это даст общую энергию 14,25

Обратите внимание, что разрешение дробных значений даст вам более высокое объективное значение, чем разрешение только 0% или 100%, поэтому ни одно целочисленное решение не будет лучше 14.25 (или 14 в этом отношении, поскольку целевое значение должно быть целым числом) .

Чтобы решить исходную проблему с рюкзаком, вы можете использовать ветвление и связку, которые на практике должны прекрасно работать. Предположим, что у вас есть текущее решение кандидата с объективным значением z *

  1. Решите расслабленную задачу, где вы разрешите дробные веса. Если значение меньше z *, отбросьте эту ветвь.
  2. Вычислить новое значение z, которое является решением, найденным без последнего дробного веса, если это значение больше z *, замените z * на новое значение
  3. Выберите один элемент (скажем, первый в списке, самый прибыльный) и сформируйте две подзадачи: одна, в которой вы должны включить ее в решение, а другая, в которой не может включите его в решение (это шаг ветвления).
  4. Пока у вас остались подзадачи для решения, выберите одну и вернитесь к шагу 1.

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

Более подробное описание приведено в Ветвь и связка в Википедии.

1 голос
/ 25 марта 2009

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

0 голосов
/ 16 июля 2018

Сделал что-то похожее на это в личном проекте, но в php-коде. Если вы хотите портировать на c #, не стесняйтесь.

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

ПРИМЕЧАНИЕ. При этом учитывается, что любой элемент можно использовать 0 или 1 раз в каждом результате

<code><?php
$products = [
    ['id' => 1, 'price' => 3.00, 'energy' => 200],
    ['id' => 2, 'price' => 14.10, 'energy' => 3200],
    ['id' => 3, 'price' => 2.66, 'energy' => 300],
    ['id' => 4, 'price' => 5.00, 'energy' => 450],
    ['id' => 5, 'price' => 6.23, 'energy' => 667],
    ['id' => 6, 'price' => 7.00, 'energy' => 1200]
];

function genCombinations($values, $count = 0)
{

    // Figure out how many combinations are possible:

    $comboCount = pow(count($values) , $count);
    $r = [];

    // Iterate and add to array

    for ($i = 0; $i < $comboCount; $i++){
        $r[] = getCombination($values, $count, $i);
    }
    return $r;
}


// State-based way of generating combinations:

function getCombination($values, $count, $index)
{
    $result = [];
    for ($i = 0; $i < $count; $i++) {

        // Figure out where in the array to start from, given the external state and the internal loop state

        $pos = $index % count($values);

        // Append and continue

        $result[] = $values[$pos];
        $index = ($index - $pos) / count($values);
    }
    return $result;
}

//maximize energy for given price

function getBestProductCombinations($products,$price_limit){

    //find all combinations where each product is either selected or not - true or false

    $combos = genCombinations([true,false],count($products));

    $results = [];
    foreach($combos as $combo){

        //loop through each combination and get a result

        $sum_price = 0;$items = [];$sum_energy = 0;
        foreach($combo as $i => $o){

            //loop through the array of true/false values determining if an item is on or off

            if($o){

                //if on, add item to result

                $sum_price += $products[$i]['price'];
                $sum_energy += $products[$i]['energy'];
                $items[] = $products[$i];
            }
        }
        if($sum_price <= $price_limit){

            //if sum of result is within the price limit, add to the results array

            $results[] = [
                'items' => $items,
                'price' => $sum_price,
                'energy' => $sum_energy
            ];
        }
    }

    $best = $results[0];$ra = [$best];
    foreach($results as $k => $result){
        if($k === 0){continue;}//skip first iteration as it was set above

        //check if the energy is higher than the best, or if equal, check if the price is better

        if($result['energy'] > $best['energy'] || ($result['energy'] === $best['energy'] && $result['price'] < $best['price'])){

            //reset best to the current result, reset return array

            $best = $result;
            $ra = [$best];
        }else if($result['energy'] === $best['energy']){

            //current result is the same as best, add it to the return array

            $ra[] = $result;
        }
    }
    return $ra;
}

echo '<pre>'.json_encode(getBestProductCombinations($products,10),JSON_PRETTY_PRINT).'
';

Что тогда даст вам:

[
    {
        "items": [
            {
                "id": 3,
                "price": 2.66,
                "energy": 300
            },
            {
                "id": 6,
                "price": 7,
                "energy": 1200
            }
        ],
        "price": 9.66,
        "energy": 1500
    }
]
0 голосов
/ 27 марта 2009

Должна быть возможность решить проблему с помощью Cream for Java . Также доступна версия для C # CSharpCream .

0 голосов
/ 25 марта 2009

Это напоминает мне знаменитый алгоритм ранца

http://en.wikipedia.org/wiki/Knapsack_problem

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