Example 20.3 Integer Programming Knapsack Problem
The next example uses the integer encoding, along with user modules for crossover and mutation. It formulates the knapsack problem using fixed-length integer encoding. The integer vector solution s is a vector of ones and zeros, where s[i]=1 implies that item i is packed in the knapsack. The weight constraints of the problem are not handled explicitly, but are accounted for by including a penalty for overweight in the objective function. The crossover operator randomly chooses a value for each element of the solution vector from each parent. The mutation operator randomly changes the values of a user-set number of elements in the solution vector. For this problem the value of the global optimum is 18.
proc iml;
weight = {2 3 4 4 1 1 1 1 1 1 1 1 1 1 1};
limit = 9; /* weight limit */
reward = {6 6 6 5 1.3 1.2 1.1 1.0 1.1 1.3 1.0 1.0 0.9 0.8 0.6};
start knapsack( x ) global( weight, reward, limit);
wsum = sum(weight # x);
rew = sum(reward # x);
/* subtract penalty for exceeding weight */
if wsum>limit then
rew = rew - 5 * (wsum - limit);
return(rew);
finish;
start switch_mut(s) global(nswitches);
n = ncol(s);
do i = 1 to nswitches;
k = int(uniform(1234) * n) + 1;
if s[k]=0 then
s[k] = 1;
else
s[k] = 0;
end;
finish;
start uniform_cross(child1, child2, parent1, parent2);
child1 = parent1;
child2 = parent2;
do i = 1 to ncol(parent1);
r = uniform(1234);
if r<=0.5 then do;
child1[i] = parent2[i];
child2[i] = parent1[i];
end;
end;
finish;
id = gasetup(2,15, 123);
call gasetobj(id, 1, "knapsack"); /* maximize objective module */
call gasetcro(id, 1.0, 0,"uniform_cross"); /* user crossover module */
call gasetmut(id,
0.20, /* mutation probabilty */
0, "switch_mut" /* user mutation module */
);
nswitches = 3;
call gasetsel(id, 3, /* carry 3 over to next generation */
1, /* dual tournament */
0.95 /* best-player-wins probabilty */
);
call gainit(id,100,{0 0 0 0 0 0 0 0 0 0 0 0 0 0 0,
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1});
niter = 20;
summary = j(niter,2);
mattrib summary [c = {"bestValue", "avgValue"}];
call gagetval(value, id);
summary[1,1] = value[1];
summary[1,2] = value[:];
do i = 1 to niter;
call garegen(id);
call gagetval(value, id);
summary[i,1] = value[1];
summary[i,2] = value[:];
end;
call gagetmem(mem, value, id, 1);
print "best member " mem[f = 1.0 l = ""],
"best value " value[l = ""];
iteration = t(1:niter);
print iteration summary;
call gaend(id);
The output of the program is
best member 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
best value 18
SUMMARY
ITERATION bestValue avgValue
1 16 2.44
2 16 6.257
3 16 6.501
4 16.7 7.964
5 16.7 8.812
6 16.7 9.254
7 16.7 10.021
8 16.8 11.216
9 16.9 12.279
10 16.9 12.094
11 16.9 11.633
12 16.9 11.431
13 18 11.502
14 18 13.2
15 18 13.128
16 18 13.282
17 18 12.876
18 18 13.715
19 18 12.889
20 18 13.15
Note that for this problem, the mutation parameters are set higher than is often seen for GAs. For this example, this is necessary to prevent premature convergence.