Problem with backpack problem

18

I am working with a case of a client in which he applies the algorithm Problem of the backpack . I am using the code that I attached, it works more or less and has errors.

The example has 12 packages of different weights (which in their total weigh 1260 Kilos) with an associated value or price. The objective is to load the maximum number of packages, prioritizing the highest value without exceeding the maximum load of the truck.

For example;

1.- If the maximum load of the truck is 9 Kilos, this code should select Paquete 2 Peso :9 valor :160 and not Paquete 2 Peso :9 valor :150 (But it does not, select zero).

2.- On the other hand, if the maximum load of the truck is 500 Kilos, it will select the following:

Paquete 9 Peso :230 valor :591
Paquete 3 Peso :153 valor :200
Paquete 4 Peso :50 valor :160
Paquete 2 Peso :9 valor :160
Paquete 1 Peso :9 valor :150

Peso total paquetes: 451
Valor total paquetes: 1261

Does the job but not very well (7 and 5 would be missing to complete a load of 493)

3.- If the maximum load of the truck is 230, it will select the following:

Paquete 9 Peso :9 valor :230 

If you do the work but wrong, because the best option would be the following:

Paquetes 1, 2, 4 y 5
Con un peso peso total de 83
y un valor valor total de 530 

Since it is more efficient.

Code :

from itertools import takewhile

#Paquetes: "Nombre del paquete", Kilos, Precio
PAQUETES = (
    ("Paquete 1", 9, 150), ("Paquete 2", 9, 160), ("Paquete 3", 153, 200), ("Paquete 4", 50, 160),
    ("Paquete 5", 15, 60), ("Paquete 6", 66, 45), ("Paquete 7", 27, 60), ("Paquete 8", 39, 40),
    ("Paquete 9", 230, 591), ("Paquete 10", 520, 10), ("Paquete 11", 110, 70), ("Paquete 12", 32, 30))

def proceso_valor(item):
    nombre, peso, valor = item
    return float(valor)

def proceso_peso(item):
    nombre, peso, valor = item
    proceso_peso.peso_maximo -= peso
    return proceso_peso.peso_maximo >= 0    

#carga máxima del camión
proceso_peso.peso_maximo = 750


carga_lista = list(takewhile(proceso_peso, reversed(sorted(PAQUETES, key=proceso_valor))))

sumacarga = 0
sumavalor = 0

for item in carga_lista:
    print item[0] + ' Peso :%i' % item[1] + ' valor :%i' % item[2]
    sumacarga = sumacarga + item[1]
    sumavalor = sumavalor + item[2]

print ''
print 'Peso total paquetes: %i' % sumacarga
print 'Valor total paquetes: %i' % sumavalor

There is something I am not seeing, I need help and the questions are: What makes my code not work with small maximum load values (9 or 100 for example)? and What mathematical operation is missing here to improve the result?

    
asked by Eduardo Munizaga 27.02.2016 в 04:44
source

2 answers

12

Est is simpler than it seems: What you lack is a price / weight ratio because what you do is that the function process_value returns only one data from the equation Where do you leave the weight? if what you want is the maximum number of packages, prioritizing the highest value you have to divide the price by the value so that the sorted returns a related key. I do not know if I explain myself but what you have to do is this:

def proceso_valor(item):
    nombre, peso, valor = item
    return float(valor)/ float(peso)

and voila, that way the sorted will give you your 4 elements where the room will be that relationship you are looking for. If you try it with proceso_peso.peso_maximo = 500, it gives you a load equal to 493 and not 451 as you say with the two missing packages.

    
answered by 01.03.2016 / 15:03
source
3

The problem of the backpack is an NP problem, that is, you will not find an algorithm that always obtains the optimal solution. However, what you can do is discard the worst cases to stay with the best possible solutions.

That said, your algorithm should have two objectives:

  • Insert as many packages
  • Among the solutions obtained, stay with those that add more value
  • In your approach it is not clear if prioritizing by value is to put the most expensive packages first or that the total sum is the highest. I suppose that is the latter case. Although the problem can also be posed the other way around: wanting to carry the most expensive load, without exceeding the load limit of the truck.

    Well, your algorithm starts by sorting by value and then staying with those that fit in the maximum load. Umm! It is just the opposite of what you should try. To start, all the expensive packages are subtracting from the maximum load. For example, try two packages:

    proceso_peso.peso_maximo = 750
    
    PAQUETES = (
        ("Paquete 1", 9, 150), ("Paquete 2", 1000, 10000)
        )
    
    carga_lista = list(takewhile(proceso_peso, reversed(sorted(PAQUETES, key=proceso_valor))))
    

    The Package 2 has a lot of value and a lot of weight. When checking with proceso_peso you subtract your weight from proceso_peso.peso_maximo , which is negative, and takewhile ends up returning an empty list.

    Once the failure is seen, my advice is that you do not use function attributes and, more importantly, do not change the problem conditions in an iteration (and if you do, make it clear ). For the former, use object-oriented programming; for the latter, use functional programming. For both, use python.

    The problem of the backpack is well studied. Surely you find several algorithms that try to find the solution in a more or less fast way. In case it's a clue, I'll tell you how it would be done by "brute force" , although it takes a lot of time in some cases:

    from operator import itemgetter
    
    #Paquetes: "Nombre del paquete", Kilos, Precio
    PAQUETES = (
        ("Paquete 1", 9, 150), ("Paquete 2", 9, 160), ("Paquete 3", 153, 200), ("Paquete 4", 50, 160),
        ("Paquete 5", 15, 60), ("Paquete 6", 66, 45), ("Paquete 7", 27, 60), ("Paquete 8", 39, 40),
        ("Paquete 9", 230, 591), ("Paquete 10", 520, 10), ("Paquete 11", 110, 70), ("Paquete 12", 32, 30))
    
    #carga máxima del camión
    PESOMAXIMO = 230
    
    # Útiles para acceso al peso y valores (irían mejor definiendo una clase)
    get_peso = itemgetter(1)
    get_valor = itemgetter(2)
    
    def total_peso(paquetes):
        return sum(get_peso(x) for x in paquetes)
    
    def total_valor(paquetes):
        return sum(get_valor(x) for x in paquetes)
    
    # Obtención de todas las combinaciones posibles
    # Función recursiva
    def combinaciones(paquetes, peso_maximo):
        paqs = [ p for p in paquetes if get_peso(p) <= peso_maximo ]
        resultado = []
        for p in paqs:
            res = combinaciones([x for x in paqs if x!=p], peso_maximo - get_peso(p))
            if len(res) == 0:
                resultado.append([p])
            else:
                resultado.extend([[p]+x for x in res])
        return resultado
    
    # solución
    max(combinaciones(PAQUETES, PESOMAXIMO), key=total_valor)
    
        
    answered by 27.02.2016 в 15:07