Line data Source code
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, 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 : #include "utils/palloc.h"
36 :
37 : /*
38 : * DiscreteKnapsack
39 : *
40 : * The item_values input is optional; if omitted, all the items are assumed to
41 : * have value 1.
42 : *
43 : * Returns a Bitmapset of the 0..(n-1) indexes of the items chosen for
44 : * inclusion in the solution.
45 : *
46 : * This uses the usual dynamic-programming algorithm, adapted to reuse the
47 : * memory on each pass (by working from larger weights to smaller). At the
48 : * start of pass number i, the values[w] array contains the largest value
49 : * computed with total weight <= w, using only items with indices < i; and
50 : * sets[w] contains the bitmap of items actually used for that value. (The
51 : * bitmapsets are all pre-initialized with an unused high bit so that memory
52 : * allocation is done only once.)
53 : */
54 : Bitmapset *
55 40 : DiscreteKnapsack(int max_weight, int num_items,
56 : int *item_weights, double *item_values)
57 : {
58 40 : MemoryContext local_ctx = AllocSetContextCreate(CurrentMemoryContext,
59 : "Knapsack",
60 : ALLOCSET_SMALL_MINSIZE,
61 : ALLOCSET_SMALL_INITSIZE,
62 : ALLOCSET_SMALL_MAXSIZE);
63 40 : MemoryContext oldctx = MemoryContextSwitchTo(local_ctx);
64 : double *values;
65 : Bitmapset **sets;
66 : Bitmapset *result;
67 : int i,
68 : j;
69 :
70 40 : Assert(max_weight >= 0);
71 40 : Assert(num_items > 0 && item_weights);
72 :
73 40 : values = palloc((1 + max_weight) * sizeof(double));
74 40 : sets = palloc((1 + max_weight) * sizeof(Bitmapset *));
75 :
76 2320 : for (i = 0; i <= max_weight; ++i)
77 : {
78 2280 : values[i] = 0;
79 2280 : sets[i] = bms_make_singleton(num_items);
80 : }
81 :
82 112 : for (i = 0; i < num_items; ++i)
83 : {
84 72 : int iw = item_weights[i];
85 72 : double iv = item_values ? item_values[i] : 1;
86 :
87 4946 : for (j = max_weight; j >= iw; --j)
88 : {
89 4874 : int ow = j - iw;
90 :
91 4874 : if (values[j] <= values[ow] + iv)
92 : {
93 : /* copy sets[ow] to sets[j] without realloc */
94 4874 : if (j != ow)
95 : {
96 1664 : sets[j] = bms_del_members(sets[j], sets[j]);
97 1664 : sets[j] = bms_add_members(sets[j], sets[ow]);
98 : }
99 :
100 4874 : sets[j] = bms_add_member(sets[j], i);
101 :
102 4874 : values[j] = values[ow] + iv;
103 : }
104 : }
105 : }
106 :
107 40 : MemoryContextSwitchTo(oldctx);
108 :
109 40 : result = bms_del_member(bms_copy(sets[max_weight]), num_items);
110 :
111 40 : MemoryContextDelete(local_ctx);
112 :
113 40 : return result;
114 : }
|