Está en la página 1de 9

PROBLEMA DE LA MOCHILA

(KNAPSACK PROBLEM)

1. INTRODUCCIÓN

El problema de la mochila es un problema simple de entender: hay una


persona que tiene una mochila con una cierta capacidad y tiene que elegir qué
elementos pondrá en ella. Cada uno de los elementos tiene un peso y aporta
un beneficio. El objetivo de la persona es elegir los elementos que le permitan
maximizar el beneficio sin excederse de la capacidad permitida.

A la vez es un problema complejo, si por complejidad nos referimos a la


computacional. “Un problema se cataloga como inherentemente difícil si su
solución requiere de una cantidad significativa de recursos computacionales,
sin importar el algoritmo utilizado.” El problema de la mochila forma parte de
una lista histórica de problemas NP − Completos elaborada por Richard Karp
en 1972.

En el caso del problema de la mochila, si contáramos con 4 productos, para


saber cuál es la mejor solución podríamos probar las 2 4 = 16 posibilidades. El 2
se desprende del hecho de que cada decisión es incluir o no al producto y el 4
de la cantidad de productos. 16 posibilidades es un número manejable, sin
embargo, si la cantidad de elementos por ejemplo ascendiera a 20, tendríamos
que analizar nada más y nada menos que 220 = 1,048,576 posibilidades.

2. DEFINICIÓN FORMAL DEL PROBLEMA

Supongamos que tenemos n distintos tipos de ítems, que van del 1 al n. De


cada tipo de ítem se tienen qi ítems disponibles, donde qi es un entero positivo
que cumple: 1≤ q i ≤ ∞

Cada tipo de ítem i tiene un beneficio asociado dado por vi y un peso (o


volumen) wi. Usualmente se asume que el beneficio y el peso no son
negativos. Para simplificar la representación, se suele asumir que los ítems
están listados en orden creciente según el peso (o volumen).
Por otro lado se tiene una mochila, donde se pueden introducir los ítems, que
soporta un peso máximo (o volumen máximo) W.
El problema consiste en meter en la mochila ítems de tal forma que
se maximice el valor de los ítems que contiene y siempre que no se
supere el peso máximo que puede soportar la misma. La solución al
problema vendrá dado por la secuencia de variables x1, x2, ..., xn donde el
valor de xi indica cuantas copias se meterán en la mochila del tipo de ítem i.
El problema se puede expresar matemáticamente por medio del
siguiente programa lineal:
n
Maximizar ∑ vi xi
i=1
n
Tal que ∑ vi xi ≤ W
i=1

Y 1≤ q i ≤ ∞

Si qi =1 para i=1,2,...,n se dice que se trata del problema de la mochilla


0-1. Si uno o más qi es infinito entonces se dice que se trata del problema
de la mochila no acotado también llamado a veces problema de la
mochila entera. En otro caso se dice que se trata del problema de la
mochila acotado.

3. ALGORITMO (PARADIGMA: DIVIDE AND CONQUER)

Para que el algoritmo propuesto funcione correctamente los elementos a


insertar en la mochila se han de ordenar en función de la relación
peso/beneficio:
b1 / p1 ≥ b 2 / p2 … ≥ bn / pn

El Quicksort trabaja particionando el conjunto a ordenar en dos partes, para


después ordenar dichas partes independientemente. El punto clave del
algoritmo está en el procedimiento que divide el conjunto. El proceso de
división del conjunto debe cumplir las siguientes tres condiciones:

a. El elemento pivote=a[i] está en su posición final en el array para algún


índice i.
b. Todos los elementos en a[first], ..., a[i-1] son menores o iguales a a[i].
c. Todos los elementos en a[i+1], ..., a[last] son mayores que a[i].

En este punto se aplica el mismo método recursivamente a los dos


subproblemas generados: a[first], ... , a[i-1] y a[i+1], ... , a[last]. El resultado
final será una matriz completamente ordenada, y por tanto no hace falta un
paso subsiguiente de combinación.

4. EL ALGORITMO

A. ANÁLISIS DEL PROBLEMA

Para el análisis tomaremos un caso en particular.


Sea el conjunto de elementos (6) con las siguientes características:

0 1 2 3 4 5
VALOR 50 40 30 66 20 60
PESO 60 40 20 30 10 50

Y una mochila con capacidad de 100.

a. VORAZ SIN ORDENAMIENTO

Si tomamos los elementos tal cual son ingresados (sin ordenamiento


previo). Tomaríamos los 2 primeros:

VALOR PESO FRACCIÓ


N
50 60 1
40 40 1

Valor máximo obtenido: 90

b. MAYOR VALOR

Si consideramos como parámetro de decisión el valor de los objetos a


escoger, tomaríamos los siguientes elementos:

VALO PESO FRACCIÓ


R N
66 30 1
60 50 1
50 60 1/3

 Para el caso de MOCHILA FRACCIONARIA debemos tomar solo una


porción del último elemento para no excedernos en el peso.
Valor máximo obtenido: 142.667

VALOR PESO
66 30
 Para el caso de 60 50 MOCHILA NO FRACCIONARIA
tomamos el 30 20 siguiente elemento con
mayor valor cuyo peso sea menor o igual al peso restante antes de
sobrepasar la capacidad de la mochila.
Valor máximo obtenido: 156

Observamos mejores resultados respecto del caso anterior.

c. MENOR PESO

Si consideramos como parámetro de decisión el menor peso de los


objetos a escoger, tendríamos la siguiente solución

VALO PESO FRACCIÓ


R N
20 10 1
30 20 1
66 30 1
40 40 1

Valor máximo obtenido: 156

Observamos mejores resultados respecto del caso anterior.

d. MEJOR RENTABILIDAD

Concluimos que si tomamos en cuenta algún parámetro de ordenación


previa de los vectores PESO y VALOR obtenemos mejores soluciones
para el problema. Sin embargo, aún podemos optimizar la solución si
consideramos los dos parámetros a la vez. ¿Cómo? Relacionándolos.
¿Cómo los relacionamos? Con un nuevo parámetro al que llamaremos:
RENTABILIDAD, que viene a ser la relación inversa entre VALOR y
PESO.
VALOR
RENTABILIDAD=
PESO

De esta manera generamos un nuevo vector RENTA a partir de los 2


iniciales:

0 1 2 3 4 5
VALOR 50 40 30 66 20 60
PESO 60 40 20 30 10 50
RENTA 0.83 1 1.5 2.2 2 1.2

 Para el caso de MOCHILA FRACCIONARIA tomaremos los elementos


siguientes:

RENT VALO PESO FRACCIÓ


A R N
2.2 66 30 1
2 20 10 1
1.5 30 20 1
1.2 60 50 4/5

Valor máximo obtenido: 164

 Para el caso de MOCHILA NO FRACCIONARIA tomaremos los


elementos siguientes:

RENT VALO PESO


A R
2.2 66 30
2 20 10
1.5 30 20
1 40 40

Valor máximo obtenido: 156

B. IMPLEMENTACIÓN DEL ALGORITMO

a. ORDENAMIENTO

Realizamos el ordenamiento mediante el algoritmo de ordenamiento


Quicksort basado en el paradigma DnC (Divide y Conquer).
void quicksort(float v[], float w[],int inf,int sup)
{ int i,j,div; double pivote, tmp, tmp2, a, b;
div=(inf+sup)/2;
pivote=v[div]/w[div];
i=inf; j=sup;
do
{ while((v[i]/w[i])>pivote)i++;
while((v[j]/w[j])<pivote)j--;
if(i<=j)
{ tmp=v[j];
tmp2=w[j];
v[j]=v[i];
w[j]=w[i];
v[i]=tmp;
w[i]=tmp2;
i++; j--;
}
}while(i<=j);
if(inf<j) quicksort(v,w,inf,j);
if(i<sup) quicksort(v,w,i,sup);
}

b. SELECCIÓN DE ELEMENTOS

Implementamos un algoritmo simple para generar la solución con los


vectores ya ordenados

 Para el caso de MOCHILA FRACCIONARIA:

while(peso<capacidad&&i<n)
{ if(peso+w[i]<=capacidad)
solucion[i]=1;
else
solucion[i]=(capacidad-peso)/w[i];
peso=peso+w[i]*solucion[i];
i++;
}
 Para el caso de MOCHILA NO FRACCIONARIA:

while(peso<capacidad&&i<n)
{ if(peso+w[i]<=capacidad)
solucion[i]=1;

peso=peso+w[i]*solucion[i];
i++;
}
ALGORITMO FINAL:

#include<conio.h>
#include<iostream>
using namespace std;
int n;
void quicksort(float v[], float w[],int inf,int sup)
{ int i,j,div; double pivote, tmp, tmp2;
div=(inf+sup)/2;
pivote=v[div]/w[div];
i=inf; j=sup;
do
{ while((v[i]/w[i])>pivote)i++;
while((v[j]/w[j])<pivote)j--;
if(i<=j)
{ tmp=v[j];
tmp2=w[j];
v[j]=v[i];
w[j]=w[i];
v[i]=tmp;
w[i]=tmp2;
i++; j--;
}
}while(i<=j);
if(inf<j) quicksort(v,w,inf,j);
if(i<sup) quicksort(v,w,i,sup);
}
/*---------------------------------------------------------*/
int main ()
{ int i, j;
float peso=0, valor=0, suma=0, capacidad;
system("color 0B");
cout<<"\n\n ---------------------[ KNAPSACK
PROBLEM ]----------------------";
cout<<"\n Divide and Conquer\n\n";
cout<<"\n\t CAPACIDAD DE LA MOCHILA: ";cin>>capacidad;
cout<<"\n\t CANTIDAD DE OBJETOS: ";cin>>n;
float w[n], v[n], solucion[n];
cout<<endl;
for(i=0;i<n;i++)
{ cout<<"\n\t Valor del objeto "<<i+1<<": "; cin>>v[i];
cout<<"\t Peso del objeto "<<i+1<<": "; cin>>w[i]; suma=suma+w[i];
}
if(suma>=capacidad)
{ for(i=0;i<n;i++)
solucion[i]=0;
quicksort(v,w,0,n-1);
i=0;
while(peso<capacidad&&i<n)
{ if(peso+w[i]<=capacidad)
solucion[i]=1;
else
solucion[i]=(capacidad-peso)/w[i];
peso=peso+w[i]*solucion[i];
i++;
}
system("cls");
cout<<"\n\n ---------------------[ MOCHILA
FRACCIONARIA ]----------------------\n";
cout<<"\n\tRENTA\tPESO\tVALOR\tFRACCION PESO EN MOCHILA\n\n";
for(i=0;i<n;i++)
if(solucion[i])
{ cout<<"\t "<<v[i]/w[i]<<"\t "<<w[i]<<"\t "<<v[i]<<"\t
"<<solucion[i]<<"\t\t "<<w[i]*solucion[i]<<endl;
valor=valor+v[i]*solucion[i];
}
cout<<"\n\t\t\t\t VALOR LOGRADO: "<<valor;
/* mochila no fraccionaria*/
for(i=0;i<n;i++)
solucion[i]=0;
i=0; peso=0; valor=0;
while(peso<capacidad&&i<n)
{ if(peso+w[i]<=capacidad)
solucion[i]=1;
peso=peso+w[i]*solucion[i];
i++;
}
cout<<"\n\n\n\n ---------------------[ MOCHILA NO
FRACCIONARIA ]----------------------\n";
cout<<"\n\tRENTA\tPESO\tVALOR\tFRACCION PESO EN MOCHILA\n\n";
for(i=0;i<n;i++)
if(solucion[i])
{ cout<<"\t "<<v[i]/w[i]<<"\t "<<w[i]<<"\t "<<v[i]<<"\t
"<<solucion[i]<<"\t\t "<<w[i]*solucion[i]<<endl;
valor=valor+v[i]*solucion[i];
}
cout<<"\n\t\t\t\t VALOR LOGRADO: "<<valor;

}
else cout<<"\n\n SOUCION TRIVIAL...";
getch();
return 0;
}

5. COMPLEJIDAD

En nuestro algoritmo la función del tiempo de ejecución estaría dada por el


algoritmo de ordenación (Quicksort) y el de selección de los elementos.
Como sabemos la complejidad de un Quicksort es O ( nlogn ) y por simple
inspección obtenemos la complejidad del algoritmo de selección igual a O ( n )
. Entonces la función T.E. sería la siguiente:

T ( n )=O ( nlogn ) +O(n)

Para efecto de simplificación la complejidad de nuestro algoritmo sería


O ( nlogn )

También podría gustarte