/* ks.c (0-1 knapsack problem) */ /*********************************************************************** * This code is part of GLPK (GNU Linear Programming Kit). * * Copyright (C) 2017-2018 Andrew Makhorin, Department for Applied * Informatics, Moscow Aviation Institute, Moscow, Russia. All rights * reserved. E-mail: . * * GLPK is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GLPK is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with GLPK. If not, see . ***********************************************************************/ #include "env.h" #include "ks.h" #include "mt1.h" /*********************************************************************** * 0-1 knapsack problem has the following formulation: * * maximize z = sum{j in 1..n} c[j]x[j] (1) * * s.t. sum{j in 1..n} a[j]x[j] <= b (2) * * x[j] in {0, 1} for all j in 1..n (3) * * In general case it is assumed that the instance is non-normalized, * i.e. parameters a, b, and c may have any sign. ***********************************************************************/ /*********************************************************************** * ks_enum - solve 0-1 knapsack problem by complete enumeration * * This routine finds optimal solution to 0-1 knapsack problem (1)-(3) * by complete enumeration. It is intended mainly for testing purposes. * * The instance to be solved is specified by parameters n, a, b, and c. * Note that these parameters can have any sign, i.e. normalization is * not needed. * * On exit the routine stores the optimal point found in locations * x[1], ..., x[n] and returns the optimal objective value. However, if * the instance is infeasible, the routine returns INT_MIN. * * Since the complete enumeration is inefficient, this routine can be * used only for small instances (n <= 20-30). */ #define N_MAX 40 int ks_enum(int n, const int a[/*1+n*/], int b, const int c[/*1+n*/], char x[/*1+n*/]) { int j, s, z, z_best; char x_best[1+N_MAX]; xassert(0 <= n && n <= N_MAX); /* initialization */ memset(&x[1], 0, n * sizeof(char)); z_best = INT_MIN; loop: /* compute constraint and objective at current x */ s = z = 0; for (j = 1; j <= n; j++) { if (x[j]) s += a[j], z += c[j]; } /* check constraint violation */ if (s > b) goto next; /* check objective function */ if (z_best < z) { /* better solution has been found */ memcpy(&x_best[1], &x[1], n * sizeof(char)); z_best = z; } next: /* generate next x */ for (j = 1; j <= n; j++) { if (!x[j]) { x[j] = 1; goto loop; } x[j] = 0; } /* report best (optimal) solution */ memcpy(&x[1], &x_best[1], n * sizeof(char)); return z_best; } /*********************************************************************** * reduce - prepare reduced instance of 0-1 knapsack * * Given original instance of 0-1 knapsack (1)-(3) specified by the * parameters n, a, b, and c this routine transforms it to equivalent * reduced instance in the same format. The reduced instance is * normalized, i.e. the following additional conditions are met: * * n >= 2 (4) * * 1 <= a[j] <= b for all j in 1..n (5) * * sum{j in 1..n} a[j] >= b+1 (6) * * c[j] >= 1 for all j in 1..n (7) * * The routine creates the structure ks and stores there parameters n, * a, b, and c of the reduced instance as well as template of solution * to original instance. * * Normally the routine returns a pointer to the structure ks created. * However, if the original instance is infeasible, the routine returns * a null pointer. */ struct ks { int orig_n; /* original problem dimension */ int n; /* reduced problem dimension */ int *a; /* int a[1+orig_n]; */ /* a{j in 1..n} are constraint coefficients (2) */ int b; /* b is constraint right-hand side (2) */ int *c; /* int c[1+orig_n]; */ /* c{j in 1..n} are objective coefficients (1) */ int c0; /* c0 is objective constant term */ char *x; /* char x[1+orig_n]; */ /* x{j in 1..orig_n} is solution template to original instance: * x[j] = 0 x[j] is fixed at 0 * x[j] = 1 x[j] is fixed at 1 * x[j] = 0x10 x[j] = x[j'] * x[j] = 0x11 x[j] = 1 - x[j'] * where x[j'] is corresponding solution to reduced instance */ }; static void free_ks(struct ks *ks); static struct ks *reduce(const int n, const int a[/*1+n*/], int b, const int c[/*1+n*/]) { struct ks *ks; int j, s; xassert(n >= 0); /* initially reduced instance is the same as original one */ ks = talloc(1, struct ks); ks->orig_n = n; ks->n = 0; ks->a = talloc(1+n, int); memcpy(&ks->a[1], &a[1], n * sizeof(int)); ks->b = b; ks->c = talloc(1+n, int); memcpy(&ks->c[1], &c[1], n * sizeof(int)); ks->c0 = 0; ks->x = talloc(1+n, char); /* make all a[j] non-negative */ for (j = 1; j <= n; j++) { if (a[j] >= 0) { /* keep original x[j] */ ks->x[j] = 0x10; } else /* a[j] < 0 */ { /* substitute x[j] = 1 - x'[j] */ ks->x[j] = 0x11; /* ... + a[j]x[j] + ... <= b * ... + a[j](1 - x'[j]) + ... <= b * ... - a[j]x'[j] + ... <= b - a[j] */ ks->a[j] = - ks->a[j]; ks->b += ks->a[j]; /* z = ... + c[j]x[j] + ... + c0 = * = ... + c[j](1 - x'[j]) + ... + c0 = * = ... - c[j]x'[j] + ... + (c0 + c[j]) */ ks->c0 += ks->c[j]; ks->c[j] = - ks->c[j]; } } /* now a[j] >= 0 for all j in 1..n */ if (ks->b < 0) { /* instance is infeasible */ free_ks(ks); return NULL; } /* build reduced instance */ for (j = 1; j <= n; j++) { if (ks->a[j] == 0) { if (ks->c[j] <= 0) { /* fix x[j] at 0 */ ks->x[j] ^= 0x10; } else { /* fix x[j] at 1 */ ks->x[j] ^= 0x11; ks->c0 += ks->c[j]; } } else if (ks->a[j] > ks->b || ks->c[j] <= 0) { /* fix x[j] at 0 */ ks->x[j] ^= 0x10; } else { /* include x[j] in reduced instance */ ks->n++; ks->a[ks->n] = ks->a[j]; ks->c[ks->n] = ks->c[j]; } } /* now conditions (5) and (7) are met */ /* check condition (6) */ s = 0; for (j = 1; j <= ks->n; j++) { xassert(1 <= ks->a[j] && ks->a[j] <= ks->b); xassert(ks->c[j] >= 1); s += ks->a[j]; } if (s <= ks->b) { /* sum{j in 1..n} a[j] <= b */ /* fix all remaining x[j] at 1 to obtain trivial solution */ for (j = 1; j <= n; j++) { if (ks->x[j] & 0x10) ks->x[j] ^= 0x11; } for (j = 1; j <= ks->n; j++) ks->c0 += ks->c[j]; /* reduced instance is empty */ ks->n = 0; } /* here n = 0 or n >= 2 due to condition (6) */ xassert(ks->n == 0 || ks->n >= 2); return ks; } /*********************************************************************** * restore - restore solution to original 0-1 knapsack instance * * Given optimal solution x{j in 1..ks->n} to the reduced 0-1 knapsack * instance (previously prepared by the routine reduce) this routine * constructs optimal solution to the original instance and stores it * in the array ks->x{j in 1..ks->orig_n}. * * On exit the routine returns optimal objective value for the original * instance. * * NOTE: This operation should be performed only once. */ static int restore(struct ks *ks, char x[]) { int j, k, z; z = ks->c0; for (j = 1, k = 0; j <= ks->orig_n; j++) { if (ks->x[j] & 0x10) { k++; xassert(k <= ks->n); xassert(x[k] == 0 || x[k] == 1); if (ks->x[j] & 1) ks->x[j] = 1 - x[k]; else ks->x[j] = x[k]; if (x[k]) z += ks->c[k]; } } xassert(k == ks->n); return z; } /*********************************************************************** * free_ks - deallocate structure ks * * This routine frees memory previously allocated to the structure ks * and all its components. */ static void free_ks(struct ks *ks) { xassert(ks != NULL); tfree(ks->a); tfree(ks->c); tfree(ks->x); tfree(ks); } /*********************************************************************** * ks_mt1 - solve 0-1 knapsack problem with Martello & Toth algorithm * * This routine finds optimal solution to 0-1 knapsack problem (1)-(3) * with Martello & Toth algorithm MT1. * * The instance to be solved is specified by parameters n, a, b, and c. * Note that these parameters can have any sign, i.e. normalization is * not needed. * * On exit the routine stores the optimal point found in locations * x[1], ..., x[n] and returns the optimal objective value. However, if * the instance is infeasible, the routine returns INT_MIN. * * REFERENCES * * S.Martello, P.Toth. Knapsack Problems: Algorithms and Computer Imp- * lementations. John Wiley & Sons, 1990. */ struct mt { int j; float r; /* r[j] = c[j] / a[j] */ }; static int CDECL fcmp(const void *p1, const void *p2) { if (((struct mt *)p1)->r > ((struct mt *)p2)->r) return -1; else if (((struct mt *)p1)->r < ((struct mt *)p2)->r) return +1; else return 0; } static int mt1a(int n, const int a[], int b, const int c[], char x[]) { /* interface routine to MT1 */ struct mt *mt; int j, z, *p, *w, *x1, *xx, *min, *psign, *wsign, *zsign; xassert(n >= 2); /* allocate working arrays */ mt = talloc(1+n, struct mt); p = talloc(1+n+1, int); w = talloc(1+n+1, int); x1 = talloc(1+n+1, int); xx = talloc(1+n+1, int); min = talloc(1+n+1, int); psign = talloc(1+n+1, int); wsign = talloc(1+n+1, int); zsign = talloc(1+n+1, int); /* reorder items to provide c[j] / a[j] >= a[j+1] / a[j+1] */ for (j = 1; j <= n; j++) { mt[j].j = j; mt[j].r = (float)c[j] / (float)a[j]; } qsort(&mt[1], n, sizeof(struct mt), fcmp); /* load instance parameters */ for (j = 1; j <= n; j++) { p[j] = c[mt[j].j]; w[j] = a[mt[j].j]; } /* find optimal solution */ z = mt1(n, p, w, b, x1, 1, xx, min, psign, wsign, zsign); xassert(z >= 0); /* store optimal point found */ for (j = 1; j <= n; j++) { xassert(x1[j] == 0 || x1[j] == 1); x[mt[j].j] = x1[j]; } /* free working arrays */ tfree(mt); tfree(p); tfree(w); tfree(x1); tfree(xx); tfree(min); tfree(psign); tfree(wsign); tfree(zsign); return z; } int ks_mt1(int n, const int a[/*1+n*/], int b, const int c[/*1+n*/], char x[/*1+n*/]) { struct ks *ks; int j, s1, s2, z; xassert(n >= 0); /* prepare reduced instance */ ks = reduce(n, a, b, c); if (ks == NULL) { /* original instance is infeasible */ return INT_MIN; } /* find optimal solution to reduced instance */ if (ks->n > 0) mt1a(ks->n, ks->a, ks->b, ks->c, x); /* restore solution to original instance */ z = restore(ks, x); memcpy(&x[1], &ks->x[1], n * sizeof(char)); free_ks(ks); /* check solution found */ s1 = s2 = 0; for (j = 1; j <= n; j++) { xassert(x[j] == 0 || x[j] == 1); if (x[j]) s1 += a[j], s2 += c[j]; } xassert(s1 <= b); xassert(s2 == z); return z; } /*********************************************************************** * ks_greedy - solve 0-1 knapsack problem with greedy heuristic * * This routine finds (sub)optimal solution to 0-1 knapsack problem * (1)-(3) with greedy heuristic. * * The instance to be solved is specified by parameters n, a, b, and c. * Note that these parameters can have any sign, i.e. normalization is * not needed. * * On exit the routine stores the optimal point found in locations * x[1], ..., x[n] and returns the optimal objective value. However, if * the instance is infeasible, the routine returns INT_MIN. */ static int greedy(int n, const int a[], int b, const int c[], char x[]) { /* core routine for normalized 0-1 knapsack instance */ struct mt *mt; int j, s, z; xassert(n >= 2); /* reorder items to provide c[j] / a[j] >= a[j+1] / a[j+1] */ mt = talloc(1+n, struct mt); for (j = 1; j <= n; j++) { mt[j].j = j; mt[j].r = (float)c[j] / (float)a[j]; } qsort(&mt[1], n, sizeof(struct mt), fcmp); /* take items starting from most valuable ones until the knapsack * is full */ s = z = 0; for (j = 1; j <= n; j++) { if (s + a[mt[j].j] > b) break; x[mt[j].j] = 1; s += a[mt[j].j]; z += c[mt[j].j]; } /* don't take remaining items */ for (j = j; j <= n; j++) x[mt[j].j] = 0; tfree(mt); return z; } int ks_greedy(int n, const int a[/*1+n*/], int b, const int c[/*1+n*/], char x[/*1+n*/]) { struct ks *ks; int j, s1, s2, z; xassert(n >= 0); /* prepare reduced instance */ ks = reduce(n, a, b, c); if (ks == NULL) { /* original instance is infeasible */ return INT_MIN; } /* find suboptimal solution to reduced instance */ if (ks->n > 0) greedy(ks->n, ks->a, ks->b, ks->c, x); /* restore solution to original instance */ z = restore(ks, x); memcpy(&x[1], &ks->x[1], n * sizeof(char)); free_ks(ks); /* check solution found */ s1 = s2 = 0; for (j = 1; j <= n; j++) { xassert(x[j] == 0 || x[j] == 1); if (x[j]) s1 += a[j], s2 += c[j]; } xassert(s1 <= b); xassert(s2 == z); return z; } /* eof */