Se tiene una mochila de 
de capacidad 
 y 
 objetos de pesos 
 y beneficios 
. 
Se trata de encontrar la combinación óptima de objetos 
que caben en la mochila y producen el máximo beneficio.
Denotemos por 
 el valor óptimo
de la solución para el subproblema considerando
los objetos 
 y capacidad 
.
El problema consiste en encontrar
el valor de 
.
Se cumple la siguiente fórmula:
Esta fórmula nos dice que la solución para el problema 
con objetos 
 puede obtenerse 
a partir de las soluciones de los dos subproblemas
de mochila con objetos 
 que
resultan de tomar las decisiones de incluir o no
el objeto 
.
En esta sección presentamos un programa que usa 
 threads
dispuestas en pipeline para resolver el problema. 
lhp@nereida:~/Lperl/src/threads$ cat knap.dat 8 102 2 15 20 100 20 90 30 60 40 40 30 15 60 10 10 1 N=8, M = 102 weight 0 profit 0 ... N=8, M = 102 weight 0 profit 0 ... Example 2.1 pag 20 Martello and Toth sol 280
Al ejecutar el programa se muestra la solución:
lhp@nereida:~/Lperl/src/threads$ pipeknap2.pl knap.dat sol = 280 lhp@nereida:~/Lperl/src/threads$
El programa principal se limita a cargar las librerías y crear el pipeline de threads:
lhp@nereida:~/Lperl/src/threads$ cat -n pipeknap2.pl 1 #!/usr/bin/perl -w 2 use strict; 3 use threads; 4 use Thread::Queue; 5 use threads::shared; 6 use Carp; 7 use IO::File; 8 9 ### main 10 my ($M, $w, $p) = ReadKnap($ARGV[0]); 11 my $N = @$w; 12 13 &pipe($N, \&knap, $M, $w, $p);El primer argumento
$N de pipe es el número de threads,
el segundo la subrutina a ejecutar por la thread. En este caso una referencia
a la subrutina knap. Los restantes argumentos son argumentos 
pasados a la subrutina que ejecuta la thread. En este caso:
$M es la capacidad de la mochila
$w es una referencia  a la lista de pesos
$p es una referencia a la lista de beneficios (profits)
La subrutina auxiliar ReadKnap lee un fichero conteniendo una descripción
del problema de la mochila:
79  sub ReadKnap {
80    my $filename = shift;
81
82    my $file = IO::File->new("< $filename");
83    croak "Can't open $filename" unless defined($file);
84    my (@w, @p);
85
86    my $N = <$file>;
87    my $M = <$file>;
88    for (0..$N-1) {
89      $w[$_] = <$file>;
90      $p[$_] = <$file>;
91    }
92    return ($M, \@w, \@p);
93  }
$k. Coincide con el índice del objeto del
que se encarga la tarea. Por tanto esta tarea $k 
se encarga de computar los valores óptimos @f
$from_left que le comunica con el hilo encargado del 
cómputo de 
$to_right que le comunica con el hilo encargado del 
cómputo de 
$M, la lista de pesos @w y la lista de beneficios @p
40  sub knap {
41    my $k = shift();
42    my $from_left = shift();
43    my $to_right = shift();
44    ##############
45    my $M = shift();
46    my @w = @{shift()};
47    my @p = @{shift()};
48
49    my $N = @w;
50    my @f;
51    my @left;
52
53    croak "Profits and Weights don't have the same size"
54                        unless scalar(@w) == scalar(@p);
55
56    for my $c (0..$M) {
57      $f[$c] = ($w[$k] <= $c)? $p[$k] : 0;
58    }
Antes de empezar el cómputo se inicia la lista @f 
(i.e. $k cabe en una mochila de capacidad $c
se pondrá. En otro caso el valor inicial es cero.
En general, en todo pipeline hay que distinguir tres casos:
$f[$c] - 
que depende del mismo
y se envía al vecino derecho (línea 70) para que este haga lo mismo.
Este envío puede eliminarse si se trata de la última thread
$f[$M])
60    if ($k) {
61      for my $c (0..$M) {
62        $left[$c] = $from_left->dequeue();
63        if ($c >= $w[$k]) {
64          my $y = $left[$c-$w[$k]]+$p[$k];
65          $f[$c] = ($left[$c] < $y)? $y : $left[$c];
66        }
67        else {
68          $f[$c] = $left[$c];
69        }
70        $to_right->enqueue($f[$c]);
71      }
72    }
73    else { # thread virtual 0
74      $to_right->enqueue(@f);
75    }
76    print "sol = $f[-1]\n" if $k == ($N-1);
77  }
$N+1 canales. El canal $channel[$id+1]
comunica la thread $id con la thread $id+1.
$N hilos con nombres entre 0 y $N-1.
Además de los argumentos específicos de la tarea se le envían 
17  sub pipe { # crea el pipe de tamaño N
18    my $N = shift(); # número de procesadores virtuales
19    my $task = shift(); # subrutina a ejecutar
20    my @channel : shared; # array de colas
21    my @t; # array de tareas
22    # El resto de argumentos son argumentos de $task
23
24    my $id; # identificador de procesador físico
25
26    # creamos canales ...
27    for($id=0; $id <= $N; $id++) {
28      $channel[$id] = new Thread::Queue; # canal comunicando hilos $id-1 e $id
29    }
30    # creamos threads ...
31    for($id=0; $id < $N; $id++) {
32      $t[$id] = threads->new($task, $id, $channel[$id], $channel[$id+1], @_);
33    }
34    # La thread 0 no trabaja, sólo espera  ...
35    for($id=0; $id < $N; $id++) {
36      $t[$id]->join(); # join debe ser ejecutado por el padre
37    }
38  }
Puede obtener ficheros de entrada para el ejemplo en esta sección descargando el fichero knap_dat.tgz. en la página de estos apuntes.
 
