/*------------------------------------------------------------------------- * * 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 #include #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; }