Объяснение и реализация на Python

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

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

Существует несколько вариантов задачи о рюкзаке:

  • Рюкзак 0/1: каждый предмет можно либо включить в рюкзак, либо исключить из него (т. е. каждый предмет можно взять не более одного раза). Это называется задачей о рюкзаке 0/1, поскольку для каждого предмета выбор двоичен: либо вы берете его (1), либо нет (0).
  • Дробный рюкзак: предметы можно разбить на более мелкие части, поэтому вы можете взять часть предмета, а не весь предмет. Эту версию можно решить с помощью жадного алгоритма, а задачу о рюкзаке 0/1 — нет.
  • Несколько рюкзаков. Рюкзаков несколько, и цель состоит в том, чтобы оптимально наполнить каждый из них.
  • Неограниченный рюкзак: у вас есть неограниченное количество каждого предмета, и цель состоит в том, чтобы определить, сколько каждого предмета нужно включить, чтобы максимизировать общую ценность, не превышая предела веса.

Формально задачу о рюкзаке 0/1 можно описать так:

Данный:

  • n элементов
  • Массив w[1…n], представляющий веса элементов.
  • Массив v[1…n], представляющий значения элементов.
  • Максимальный вес Вт

Максимизировать:

При условии:

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

Решение грубой силы

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

Для n элементов будет2^n решений. Следовательно, временная сложность этой задачи составит O(2^n), если мы воспользуемся решением методом грубой силы. Это будет худшее решение.

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

weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
capacity = 7
items = ['item1', 'item2', 'item3', 'item4']

Давайте напишем функцию перебора.

from itertools import combinations

def knapsack_bruteforce(weights, values, capacity):
    n = len(weights)
    max_value = 0
    best_combination = []

    # Generate all possible combinations of items
    for r in range(1, n + 1):
        for combination in combinations(range(n), r):
            total_weight = sum(weights[i] for i in combination)
            total_value = sum(values[i] for i in combination)

            # Check if the total weight is within capacity and the value is greater than the current max value
            if total_weight <= capacity and total_value > max_value:
                max_value = total_value
                best_combination = combination

    # Return the items in the best combination and the max value
    return [items[i] for i in best_combination], max_value

best_items, max_val = knapsack_bruteforce(weights, values, capacity)
best_items, max_val

"""
(['item1', 'item4'], 9)
"""

Мы используем функциюcombinations из встроенного модуля Python itertools. Эта функция используется для генерации всех возможных комбинаций элементов.

Внешний цикл (for r in range(1, n + 1):) перебирает все возможные размеры комбинаций элементов от 1 до n.

Внутренний цикл (for combination in combinations(range(n), r):) генерирует все комбинации элементов размера r.

Для каждой комбинации рассчитываются общий вес и общая стоимость.

Условие if проверяет, находится ли общий вес текущей комбинации в пределах заданной емкости и превышает ли его значение текущее лучшее значение. Если это так, он обновляет лучшее решение.

Хотя метод грубой силы прост и работает для небольших задач, он крайне неэффективен для более крупных случаев, поскольку его временная сложность экспоненциальна (O(2^n)). Даже для умеренно большого количества элементов это становится вычислительно неосуществимым.

Задача о рюкзаке 0/1 из-за ее NP-трудной природы не имеет эффективного решения за полиномиальное время для всех случаев.

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

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

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

Читать далее











Источники

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

https://www.youtube.com/watch?v=Q2vDTam9qMQ