Home Manual Reference Source

src/knapsackGreedy.js

import assert from 'assert';
import {map} from '@iterable-iterator/map';
import {range} from '@iterable-iterator/range';
import {sorted} from '@iterable-iterator/sorted';
import {filter} from '@iterable-iterator/filter';

import Item from './Item.js';
import orderedByDecreasingUtility from './orderedByDecreasingUtility.js';

/**
 * 1/2-approximation to the 0-1 knapsack problem.
 * Runs in O(n log n) time.
 *
 * Let OPT' be the value of the LP relaxation.
 * Let v_i be the values of the items ordered by utility.
 * Let k be the largest k such that sum(v[:k]) <= W.
 * Let t = (W-sum(w[:k])) / w_{k+1},
 * we have t < 1 and OPT' = sum(v[:k]) + t * v_{k+1}.
 * Hence sum(v[:k+1]) > OPT' >= OPT.
 * By partition one of { sum(v[:k]) , v_{k+1} } is at least OPT / 2.
 * Assuming w_i <= W for all i in [N] each of these is feasible.
 *
 * @param {Array} v Values.
 * @param {Array} w Weights.
 * @param {Number} n Size of the problem.
 * @param {Number} W Size of the knapsack.
 * @return {Number} Lower bound on the objective value of a solution, that is
 * at least half the objective value of the optimum.
 */
const knapsackGreedy = (v, w, n, W) => {
	assert(v.length === n);
	assert(w.length === n);
	assert(W >= 0);

	const items = sorted(
		orderedByDecreasingUtility,
		filter(
			(item) => item.w <= W,
			map((i) => new Item(w[i], v[i]), range(n)),
		),
	);
	return subroutine(W, items);
};

const subroutine = (W, items) => {
	let value = 0;
	let capacity = W;
	for (const {v, w} of items) {
		if (capacity < w) return Math.max(v, value);
		capacity -= w;
		value += v;
	}

	return value;
};

export default knapsackGreedy;