2.2. Задача оптимального выбора (задача о рюкзаке)

Много важных прикладных задач (в частности задач искусственного интеллекта) можно свести к следующей:

Имеется набор из n элементов a1...an. Каждый элемент ai характеризуется определенными свойствами, например вес wi и цена ci (это могут быть размер и время, объем инвестиций и ожидаемый доход, и т.д.). Требуется найти оптимальную выборку kn из этого набора, т.е. такую для которой, например, при заданном ограничении на суммарный вес достигается максимальная стоимость. С такой проблемой сталкивается, например, путешественник, упаковывающий чемодан, суммарный вес которого ограниченWmax кг., а ценность вещей должна быть побольше или инвестор, которому надо выгодно вложить Wmax млн. руб. в какие-то из n возможных проектов, каждый из которых имеет свою стоимость и ожидаемый доход.

Дерево решений этой задачи для а1, а2 , а3 , а4 можно представить в виде:

Рис. 2.1. Двоичное дерево решений

Здесь знак + или – над ветвью, идущей от i узла соответствует включению или не включению ai в выборку. В качестве упражнения допишите все варианты этого дерева.

2.3. Метод полного перебора двоичного дерева

При решении этой задачи в соответствии со схемой, приведенной на листинге 2.1 на каждом ходу предлагается два кандидата aij={i,0}.

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

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

Листинг 2.2

Type Telem=Record c,w:Extended end;

Var wt,ct:extended;

S,Sopt:set of byte;

a:array[1..100]of Telem;

n:byte;

Procedure vbrPP1(i:byte);

Var j,k:byte;

begin

for j:=0 to 1 do begin

if j=0 then Include(s,i)

else Exclude(S,i);

if i<n then VbrPP1(i+1)

else begin

wt:=0; ct:=0;

for k:=1 to n do

If k in s then begin

wt:=wt+a[k].w; ct:=ct+a[k].c;end;

if (wt<=Wmax) and (ct>Cmax) then

begin Cmax:=ct;Sopt:=S; end;

end;

end;//j

End;

В этой процедуре вся обработка при нахождении максимального варианта суммирование текущего веса wt и стоимости ct производится после получения листа дерева.

Листинг 2.3

Procedure VbrPP2(i:byte);

Var j:byte;

begin

for j:=0 to 1 do

begin

if j=0 then begin Include(S,i);

wt:=wt+a[i].w; ct:=ct+a[i].c end

else begin Exclude(S,i);

wt:=wt-a[i].w; ct:=ct-a[i].c end;

if i<n then VbrPP2(i+1)

else if (wt<=wmax) and (ct>cmax) then

begin cmax:=ct; Sopt:=S end;

end;//j

end;//VbrPP2

В такой модификации процедуры суммирование вынесено из листа и делается за счет повторения рекурсии.

За счет этого, появляется возможность уменьшить количество вычислений, поставив проверку переполнения рюкзака:

Листинг 2.4

Procedure VbrPP3(i:byte);

Var j:byte;

begin

for j:=0 to 1 do

begin

if j=0 then begin Include(S,i);

wt:=wt+a[i].w; ct:=ct+a[i].c end

else begin Exclude(S,i);

wt:=wt-a[i].w; ct:=ct-a[i].c end;

if (wt<=wmax) then

if i<n then VbrPP2(i+1)

else if (ct>cmax) then

begin cmax:=ct; Sopt:=S end;

end;//j

end;//VbrPP3

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

Обращение: wt:=0;ct:=0;cmax:=0; S:=[]; Sopt:=[];

ввод n; wmax; a[i], i=1..n; VbrPP(1);

Печать cmax, Sopt.

Здесь введен массив элементов a[1..n], имеющих тип записи с полями w и c. При генерации текущей и оптимальной выборок используются множества S и Sopt из индексов, входящих в выборку элементов.

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

Листинг 2.5

Procedure vbrPP4(i,wt,oct:byte);