Nano Hash - криптовалюты, майнинг, программирование

Рюкзак, но допустить переполнение

Допустим, у меня есть 5 элементов (имя, размер, значение) следующим образом:

("ITEM01", 100, 10000)
("ITEM02", 24, 576)
("ITEM03", 24, 576)
("ITEM04", 51, 2500)
("ITEM05", 155, 25)

и я должен получить максимально близкое соответствие к общему размеру 150 (каждый элемент можно добавить только один раз).

Это очень похоже на задачу о рюкзаке, но не совсем так, поскольку в этом случае моим предпочтительным решением было бы ITEM01, ITEM04, что дало бы общий размер 151 (задача о рюкзаке не позволила бы мне выйти за размер = 150 и, следовательно, получить ITEM01, ITEM02 и ITEM03 с общим размером 148).

У этой проблемы есть название? (Это все еще combinatorial optimisation)? Я ищу решение для Python, но было бы полезно, если бы я знал название того, что ищу.


  • Задача рюкзака состоит в том, чтобы максимизировать стоимость при сохранении условия: общий вес ‹= вместимость. Попробуйте указать свои ограничения более формально (например, максимизировать значение с |мощность - общий вес| ‹= дельта для некоторой дельты, или максимизировать значение - стоимость * (|емкость - общий вес|). 19.03.2013
  • Какова роль стоимости предмета в вашей целевой функции? 19.03.2013
  • @jakber Я ищу комбинацию (лучшее значение), которая дает мне размер, ближайший к общему размеру. Но в отличие от ранца, он может превышать общий размер. 19.03.2013
  • Размер @NPE является наиболее важным - стремитесь приблизиться (+/-) к общему размеру 150. После этого значение должно быть максимальным (т.е. если бы у вас был случай, когда две комбинации были одинаково близки к 150, вы бы выбрали комбинация с более высокой стоимостью). 19.03.2013
  • @jakber Я думаю, что его ограничение здесь min (| total_weight-capacity |) 19.03.2013
  • Если вас вообще не волнует вес - просто выберите жадный подход (рассчитайте стоимость/вес, а затем выбирайте один элемент за раз, пока не будете удовлетворены). Если вас несколько беспокоит вес - похоже, вы можете установить штраф за лишний вес и снова выбрать жадный подход. 19.03.2013
  • @ J0HN Меня беспокоит вес (или, в данном случае, размер). Он должен быть как можно ближе к общему размеру, поэтому может сработать метод штрафа за избыточный вес. Вы все еще считаете это проблемой рюкзака, или я должен искать что-то под другим именем? 19.03.2013

Ответы:


1

Вы можете попробовать сделать это с помощью динамического программирования.

Пусть dp[k] будет равно списку элементов с суммой размеров, равной k. Изначально d[0] = [] и dp[k] = None для k > 0. Размер списка может быть ограничен суммой размеров всех элементов, назовем его S.

Что делает алгоритм, так это то, что для каждого item он переходит от i = S до i = 0 и проверяет, соответствует ли dp[i] != None, что означает, что мы знаем, что можем выбирать элементы с суммой размеров, равной i. Эти элементы находятся в списке dp[i]. Заметим, что мы можем добавить текущее item в этот список и получить набор элементов с суммой, равной i + item.size. Итак, мы назначаем dp[i + item.size] = dp[i] + [item]. Обработав все элементы, нам просто нужно начать с желаемой суммы размеров и двигаться в обоих направлениях, чтобы найти наиболее близкое соответствие.

Код:

items = [("ITEM01", 100, 10000), ("ITEM02", 24, 576), \
    ("ITEM03", 24, 576), ("ITEM04", 51, 2500), ("ITEM05", 155, 25)]
S = sum([item[1] for item in items])
dp = [None for i in xrange(S + 1)]
dp[0] = []

for item in items:
    for i in xrange(S, -1, -1):
        if dp[i] is not None and i + item[1] <= S:
            dp[i + item[1]] = dp[i] + [item]

desired_sum = 150
i = j = desired_sum

while i >= 0 and j <= S:
    if dp[i] is not None:
        print dp[i]
        break
    elif dp[j] is not None:
        print dp[j]
        break
    else:
        i -= 1
        j += 1

вывод:

[('ITEM01', 100, 10000), ('ITEM04', 51, 2500)]

Однако сложность этого решения составляет O(n*S), где n — количество элементов, а S — сумма размеров, поэтому для некоторых целей оно может быть слишком медленным. Что можно улучшить в этом решении, так это константу S. Например, вы можете установить S в 2 * desired_sum, потому что у нас есть гарантия, что мы можем взять набор элементов с суммой размеров в [0, 2 * desired_sum] (возможно, пустой набор с суммой 0). Если вы хотите взять хотя бы один предмет, вы можете взять S = max(min_item_size, 2 * desired_sum - min_item_size), где min_item_size — это минимальный размер всех предметов.

ИЗМЕНИТЬ:

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

А именно:

if dp[i] is not None and i + item[1] <= S:

должно быть:

if dp[i] is not None and i + item[1] <= S and \
    (
        dp[i + item[1]] is None
        or
        sum(set_item[2] for set_item in dp[i]) + item[2]
            > sum(set_item[2] for set_item in dp[i + item[1]])
    ):

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

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

20.03.2013
  • Большое спасибо. Да, это работает очень хорошо, насколько я могу судить (с правкой). Это может замедлить некоторые из больших чисел, которые мне, возможно, придется бросать в него, но на данный момент это кажется хорошим. 22.03.2013

  • 2

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

    Установите значение каждого предмета на вес каждого предмета и решите задачу о рюкзаке с вместимостью 150. Это даст вам максимальный размер меньше целевого (148 в вашем примере). Таким образом, максимальный размер для рассмотрения составляет 150 + (150 - 148) = 152.

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

    В противном случае решите исходную задачу о рюкзаке с вместимостью 148 и 152 и выберите решение с наибольшим значением.

    20.03.2013
  • Да, это тоже работает (жаль, что я не могу принять два ответа), и я разработал что-то очень похожее. Используя работающий решатель рюкзака, я запускаю его один раз до максимального размера моего идеального размера * 1,1, поэтому я могу принять значения на 10% больше размера. Затем я нахожу лучшее решение для идеального размера. После этого я повторяю увеличение максимального размера от идеального размера вверх, пока не найду его. Затем я могу сравнить их, прежде чем сделать окончательный выбор. Большое спасибо за вклад. 22.03.2013
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

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

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..