| /*------------------------------------------------------------------------- |
| * |
| * knapsack.c |
| * Knapsack problem solver |
| * |
| * Given input vectors of integral item weights (must be >= 0) and values |
| * (double >= 0), compute the set of items which produces the greatest total |
| * value without exceeding a specified total weight; each item is included at |
| * most once (this is the 0/1 knapsack problem). Weight 0 items will always be |
| * included. |
| * |
| * The performance of this algorithm is pseudo-polynomial, O(nW) where W is the |
| * weight limit. To use with non-integral weights or approximate solutions, |
| * the caller should pre-scale the input weights to a suitable range. This |
| * allows approximate solutions in polynomial time (the general case of the |
| * exact problem is NP-hard). |
| * |
| * Copyright (c) 2017-2021, PostgreSQL Global Development Group |
| * |
| * IDENTIFICATION |
| * src/backend/lib/knapsack.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include <math.h> |
| #include <limits.h> |
| |
| #include "lib/knapsack.h" |
| #include "miscadmin.h" |
| #include "nodes/bitmapset.h" |
| #include "utils/builtins.h" |
| #include "utils/memutils.h" |
| |
| /* |
| * DiscreteKnapsack |
| * |
| * The item_values input is optional; if omitted, all the items are assumed to |
| * have value 1. |
| * |
| * Returns a Bitmapset of the 0..(n-1) indexes of the items chosen for |
| * inclusion in the solution. |
| * |
| * This uses the usual dynamic-programming algorithm, adapted to reuse the |
| * memory on each pass (by working from larger weights to smaller). At the |
| * start of pass number i, the values[w] array contains the largest value |
| * computed with total weight <= w, using only items with indices < i; and |
| * sets[w] contains the bitmap of items actually used for that value. (The |
| * bitmapsets are all pre-initialized with an unused high bit so that memory |
| * allocation is done only once.) |
| */ |
| Bitmapset * |
| DiscreteKnapsack(int max_weight, int num_items, |
| int *item_weights, double *item_values) |
| { |
| MemoryContext local_ctx = AllocSetContextCreate(CurrentMemoryContext, |
| "Knapsack", |
| ALLOCSET_SMALL_SIZES); |
| MemoryContext oldctx = MemoryContextSwitchTo(local_ctx); |
| double *values; |
| Bitmapset **sets; |
| Bitmapset *result; |
| int i, |
| j; |
| |
| Assert(max_weight >= 0); |
| Assert(num_items > 0 && item_weights); |
| |
| values = palloc((1 + max_weight) * sizeof(double)); |
| sets = palloc((1 + max_weight) * sizeof(Bitmapset *)); |
| |
| for (i = 0; i <= max_weight; ++i) |
| { |
| values[i] = 0; |
| sets[i] = bms_make_singleton(num_items); |
| } |
| |
| for (i = 0; i < num_items; ++i) |
| { |
| int iw = item_weights[i]; |
| double iv = item_values ? item_values[i] : 1; |
| |
| for (j = max_weight; j >= iw; --j) |
| { |
| int ow = j - iw; |
| |
| if (values[j] <= values[ow] + iv) |
| { |
| /* copy sets[ow] to sets[j] without realloc */ |
| if (j != ow) |
| { |
| sets[j] = bms_del_members(sets[j], sets[j]); |
| sets[j] = bms_add_members(sets[j], sets[ow]); |
| } |
| |
| sets[j] = bms_add_member(sets[j], i); |
| |
| values[j] = values[ow] + iv; |
| } |
| } |
| } |
| |
| MemoryContextSwitchTo(oldctx); |
| |
| result = bms_del_member(bms_copy(sets[max_weight]), num_items); |
| |
| MemoryContextDelete(local_ctx); |
| |
| return result; |
| } |