Задача о рюкзаке (Knapsack problem) — это одна из классических задач комбинаторной оптимизации. Суть задачи заключается в выборе предметов для помещения в рюкзак с ограниченной вместимостью таким образом, чтобы максимизировать общую ценность выбранных предметов.
Найти такой набор предметов, который:
Максимизировать:
где xi — бинарная переменная:
Наиболее эффективный способ решения задачи о рюкзаке — использование динамического программирования.
Алгоритм работает за время O(nC), где:
Псевдокод решения:
function knapsack(values, weights, n, C): create array M[n+1][C+1] for i from 0 to n: M[i][0] = 0 for j from 0 to C: M[0][j] = 0 for i from 1 to n: for j from 1 to C: if weights[i] <= j: M[i][j] = max(M[i-1][j], values[i] + M[i-1][j-weights[i]]) else: M[i][j] = M[i-1][j] return M[n][C]
MATLAB предоставляет встроенную функцию intlinprog для решения задач целочисленного линейного программирования.
Пример входных данных:
% Вектор ценностей предметов v = [15 20 30 40]; % Вектор весов предметов w = [1 2 3 4]; % Вместимость рюкзака C = 6; % Количество предметов n = length(v);
Настройка параметров для intlinprog:
% Целевая функция (максимизация, поэтому берем с минусом) f = -v; % Матрица ограничений A = w; % Правая часть ограничения b = C; % Индексы целочисленных переменных intcon = 1:n; % Границы для переменных (0 или 1) lb = zeros(1,n); ub = ones(1,n);
Вызов функции intlinprog:
% Решение задачи [x, fval] = intlinprog(f, intcon, A, b, [], [], lb, ub); % Вывод результатов disp('Включенные предметы:'); disp(find(x == 1)); disp(['Максимальная ценность: ', num2str(-fval)]);
Метод полного перебора (brute force) заключается в переборе всех возможных комбинаций предметов и выборе оптимального решения.
Пример построения дерева решений:
[] / \ / \ / \ [1] [] / \ / \ / \ / \ [1,2] [1] [2] [] / \ / \ / \ / \ [1,2,3][1,2][1,3][1] [2,3][2][]
Пример кода для перебора:
function bruteForceKnapsack(values, weights, C) n = length(values); maxValue = 0; bestSet = []; % Перебираем все комбинации от 0 до 2^n-1 for i = 0:(2^n - 1) currentSet = dec2bin(i, n) - '0'; totalWeight = sum(currentSet .* weights); totalValue = sum(currentSet .* values); if totalWeight <= C && totalValue > maxValue maxValue = totalValue; bestSet = currentSet; end end return bestSet, maxValue; end
Начальные параметры задачи:
weights = [45, 60, 95, 50, 80]; % веса предметов values = [25, 30, 45, 28, 35]; % стоимости предметов capacity = 270; % вместимость рюкзака n = length(weights); % количество предметов
Создание всех возможных комбинаций:
all_combinations = dec2bin(0:(2^n-1)) - '0'; num_combinations = size(all_combinations, 1);
Вычисление веса и стоимости для каждой комбинации:
total_weights = zeros(num_combinations, 1); total_values = zeros(num_combinations, 1); for i = 1:num_combinations selected_items = all_combinations(i, :); total_weights(i) = sum(selected_items .* weights); total_values(i) = sum(selected_items .* values); end
Отбор допустимых комбинаций:
valid_indices = total_weights <= capacity; valid_weights = total_weights(valid_indices); valid_values = total_values(valid_indices);
Определение наилучшего варианта:
[max_value, max_index] = max(valid_values); optimal_weight = valid_weights(max_index); optimal_combination = all_combinations(max_index, :);
Построение графика:
figure; hold on; plot(valid_weights, valid_values, 'bo', 'MarkerFaceColor', [0.5 0.5 0.5]); plot(optimal_weight, max_value, 'ro', 'MarkerSize', 10); xlim([0, capacity]); ylim([0, max(valid_values) + 10]); xlabel('Общий вес'); ylabel('Общая стоимость'); title('Графическое решение задачи о рюкзаке'); grid on;
Финальные результаты:
fprintf('Оптимальное решение:\n'); fprintf('Выбранные предметы: %s\n', mat2str(optimal_combination)); fprintf('Максимальная стоимость: %d\n', max_value); fprintf('Общий вес: %d\n', optimal_weight);