Задача о рюкзаке (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);