| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * knapsack.c |
| 4 | * Knapsack problem solver |
| 5 | * |
| 6 | * Given input vectors of integral item weights (must be >= 0) and values |
| 7 | * (double >= 0), compute the set of items which produces the greatest total |
| 8 | * value without exceeding a specified total weight; each item is included at |
| 9 | * most once (this is the 0/1 knapsack problem). Weight 0 items will always be |
| 10 | * included. |
| 11 | * |
| 12 | * The performance of this algorithm is pseudo-polynomial, O(nW) where W is the |
| 13 | * weight limit. To use with non-integral weights or approximate solutions, |
| 14 | * the caller should pre-scale the input weights to a suitable range. This |
| 15 | * allows approximate solutions in polynomial time (the general case of the |
| 16 | * exact problem is NP-hard). |
| 17 | * |
| 18 | * Copyright (c) 2017-2019, PostgreSQL Global Development Group |
| 19 | * |
| 20 | * IDENTIFICATION |
| 21 | * src/backend/lib/knapsack.c |
| 22 | * |
| 23 | *------------------------------------------------------------------------- |
| 24 | */ |
| 25 | #include "postgres.h" |
| 26 | |
| 27 | #include <math.h> |
| 28 | #include <limits.h> |
| 29 | |
| 30 | #include "lib/knapsack.h" |
| 31 | #include "miscadmin.h" |
| 32 | #include "nodes/bitmapset.h" |
| 33 | #include "utils/builtins.h" |
| 34 | #include "utils/memutils.h" |
| 35 | |
| 36 | /* |
| 37 | * DiscreteKnapsack |
| 38 | * |
| 39 | * The item_values input is optional; if omitted, all the items are assumed to |
| 40 | * have value 1. |
| 41 | * |
| 42 | * Returns a Bitmapset of the 0..(n-1) indexes of the items chosen for |
| 43 | * inclusion in the solution. |
| 44 | * |
| 45 | * This uses the usual dynamic-programming algorithm, adapted to reuse the |
| 46 | * memory on each pass (by working from larger weights to smaller). At the |
| 47 | * start of pass number i, the values[w] array contains the largest value |
| 48 | * computed with total weight <= w, using only items with indices < i; and |
| 49 | * sets[w] contains the bitmap of items actually used for that value. (The |
| 50 | * bitmapsets are all pre-initialized with an unused high bit so that memory |
| 51 | * allocation is done only once.) |
| 52 | */ |
| 53 | Bitmapset * |
| 54 | DiscreteKnapsack(int max_weight, int num_items, |
| 55 | int *item_weights, double *item_values) |
| 56 | { |
| 57 | MemoryContext local_ctx = AllocSetContextCreate(CurrentMemoryContext, |
| 58 | "Knapsack" , |
| 59 | ALLOCSET_SMALL_SIZES); |
| 60 | MemoryContext oldctx = MemoryContextSwitchTo(local_ctx); |
| 61 | double *values; |
| 62 | Bitmapset **sets; |
| 63 | Bitmapset *result; |
| 64 | int i, |
| 65 | j; |
| 66 | |
| 67 | Assert(max_weight >= 0); |
| 68 | Assert(num_items > 0 && item_weights); |
| 69 | |
| 70 | values = palloc((1 + max_weight) * sizeof(double)); |
| 71 | sets = palloc((1 + max_weight) * sizeof(Bitmapset *)); |
| 72 | |
| 73 | for (i = 0; i <= max_weight; ++i) |
| 74 | { |
| 75 | values[i] = 0; |
| 76 | sets[i] = bms_make_singleton(num_items); |
| 77 | } |
| 78 | |
| 79 | for (i = 0; i < num_items; ++i) |
| 80 | { |
| 81 | int iw = item_weights[i]; |
| 82 | double iv = item_values ? item_values[i] : 1; |
| 83 | |
| 84 | for (j = max_weight; j >= iw; --j) |
| 85 | { |
| 86 | int ow = j - iw; |
| 87 | |
| 88 | if (values[j] <= values[ow] + iv) |
| 89 | { |
| 90 | /* copy sets[ow] to sets[j] without realloc */ |
| 91 | if (j != ow) |
| 92 | { |
| 93 | sets[j] = bms_del_members(sets[j], sets[j]); |
| 94 | sets[j] = bms_add_members(sets[j], sets[ow]); |
| 95 | } |
| 96 | |
| 97 | sets[j] = bms_add_member(sets[j], i); |
| 98 | |
| 99 | values[j] = values[ow] + iv; |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | MemoryContextSwitchTo(oldctx); |
| 105 | |
| 106 | result = bms_del_member(bms_copy(sets[max_weight]), num_items); |
| 107 | |
| 108 | MemoryContextDelete(local_ctx); |
| 109 | |
| 110 | return result; |
| 111 | } |
| 112 | |