简体   繁体   中英

Almost solved 0/1 Bidimensional Knapsack in python

I am trying to select at most 10 items from this list to get the higher possible profit.

  Item name | Item "Weight" | Item "Profit"

         [A, 1084, 424],
         [B, 1143, 391],
         [C, 1205, 351],
         [D, 1088, 328],
         [E, 5, 21],
         [F, 996, 241],
         [G, 13, 23],
         [H, 2, 9],
         [I, 5, 13],
         [J, 14, 21],
         [K, 11, 18],
         [L, 4, 12],
         [M, 121, 59],
         ...
    Total length: 249

I found that my problem could be defined as a 0/1 Bidimensional Knapsack problem . Weight would be the first dimension, and the 10 items limit would be the second dimension. So I tried different solutions with python to optimize the selection.

After searching different sources, I was not able to found a python solution to my specific problem:

  • Defined weight limit (Somewhere between 100-10.000)
  • Each item could be selected only once
  • Only a total of 10 items could be selected
  • Maximize the "profit"!

Finally I found this java solution that I have tried to translate, but a made a mistake somewhere, the function works almost properly but it fails to find a correct solution. I have spent a couple of days trying to find where the code fails, but it is somewhat confusing.

from collections import namedtuple

Bounty = namedtuple('Bounty', 'name value size volume')

sack =   Bounty('sack', value=0, size=800, volume=10)

items = results #A big list as mentioned above

dynNoRep = [[[0 for k in xrange(sack.volume+1)] for j in xrange(sack.size+1)] for i in xrange(
            len(items) + 1)]

for i,item in enumerate(items):
    for j in range(sack.size):
        for h in range(sack.volume):
            if item.size > j:

                #pdb.set_trace()
                dynNoRep[i][j][h] = dynNoRep[i-1][j][h]
            elif item.volume > h:
                dynNoRep[i][j][h] = dynNoRep[i-1][j][h]
            else:
                dynNoRep[i][j][h] = max(
        dynNoRep[i-1][j][h], dynNoRep[i-1][j - items[i-1].size][h - items[i-1].volume] + items[i-1].value)

for i,item in enumerate(items):
    for j in range(sack.size):
        dynNoRep[i][j][sack.volume] = dynNoRep[i][j][sack.volume-1]

j = sack.size
h = sack.volume
finalSize = 0
finalValue = 0
finalVolume = 0

for i in range(len(items)-1, -1, -1):
    #pdb.set_trace()
    if dynNoRep[i][j][h] != dynNoRep[i-1][j][h]:
        print"Value {} | Size {} | Volume {}".format(str(items[i-1].value), str(items[i-1].size), str(items[i-1].volume))
        finalSize += items[i-1].size
        finalValue += items[i-1].value
        finalVolume += items[i-1].volume
        j -= items[i - 1].size
        h -= items[i - 1].volume


print('Final size {} / {}'.format(str(finalSize), str(sack.size)))
print('Final volume {} / {}'.format(str(finalVolume), str(sack.volume)))
print('Final value {}'.format(str(finalValue)))

And this is the output, sometimes it show something nicer, but never the correct solution:

Final size 0 / 800
Final volume 0 / 10
Final value 0

Context: Windows 8.1 32 bits. Ipython notebook. Python 2.

Any suggestions to make the code works? Thank you in advance!

Finally it was easier to use Cython inside ipython, "translating" a C++ algorithm found here .

Here is the needed import:

%load_ext Cython

Be sure to install the cython extension first.

Here is the "Cython" version of the code in the previous link:

%%cython

from libc.string cimport memset

cdef int noOfItems, maxWeight, maxItems
cdef int items[100]
cdef int value[100]
cdef int dp[100][1000][100]

cdef int solve(int idx, int currentWeight, int itemsLeft, noOfItems):
    if idx == noOfItems or itemsLeft == 0:
        return 0
    if dp[idx][currentWeight][itemsLeft] != -1:
        return dp[idx][currentWeight][itemsLeft]
    cdef int v1 = 0
    cdef int v2 = 0

    if currentWeight >= items[idx]:
        v1 = solve(idx+1, currentWeight-items[idx], itemsLeft-1, noOfItems) + value[idx]
        v2 = solve(idx+1, currentWeight, itemsLeft, noOfItems)

    dp[idx][currentWeight][itemsLeft] = max(v1, v2)

    return dp[idx][currentWeight][itemsLeft]

cdef void printer(int idx, int currentWeight, int itemsLeft, noOfItems):
    if idx == noOfItems or itemsLeft == 0:
        return
    cdef int v1 = 0
    cdef int v2 = 0
    if currentWeight >= items[idx]:
        v1 = solve(idx+1, currentWeight-items[idx], itemsLeft-1, noOfItems) + value[idx]
        v2 = solve(idx+1, currentWeight, itemsLeft, noOfItems)
    if v1 >= v2:
        print(items[idx], value[idx])
        printer(idx+1, currentWeight-items[idx], itemsLeft-1, noOfItems)
        return
    else:
        printer(idx+1, currentWeight, itemsLeft, noOfItems)
        return

cdef knapsack(lenitems, maxWeight, maxItems):

    noOfItems = lenitems
    maxWeight = 15
    maxItems = 3
    pairs =  [[4,6], [3,4], [5, 5], [7, 3], [7, 7]]
    for i in range(noOfItems):
        items[i] = pairs[i][0]
        value[i] = pairs[i][1]
        memset(dp, -1, sizeof(dp))
        print(solve(0, maxWeight, maxItems, noOfItems))
        print "Printing the elements in the knapsack"
        printer(0, maxWeight, maxItems, noOfItems)

def pyknapsack(lenitems, maxWeight=15, maxItems=3):
    print 'Hao'
    knapsack(lenitems, maxWeight, maxItems)

The pyknapsack function is a python "wrapper" so you could use the C functions inside python.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM