Home Manual Reference Source

src/minima/clarkson.js

/**
 * Output sensitive inplace algorithm to find the minima set of a set S of
 * elements according to some partial order.
 *
 * Uses at most 3nA comparisons where A is the cardinality of the minima set.
 *
 * For (1), at most nA comparisons are used since we compare each element of S
 * with each elements of the minima set which is of cardinality at most A
 * during the execution of the algorithm.
 *
 * For (2), for each executed loop we
 * obtain a new minimum and increase the size of the constructed minima set by
 * 1, hence there are at most A loops execution, each of which loops over at
 * most n elements. (2) uses thus at most nA comparisons.
 *
 * The running time is dominated by the comparison time and thus the complexity
 * of this algorihtm is O(nA).
 *
 * Description and context in
 * ------------------------------------------
 * More Output-Sensitive Geometric Algorithms.
 * -------------------- Kenneth L. Clarkson -
 *
 * @param {function} prec relation
 * @param {any[]} a input array
 * @param {number} i inclusive left bound of the input
 * @param {number} j non-inclusive right bound of the input
 *
 * @return {number} non-inclusive right bound of the minima set
 */
const clarkson = (prec, a, i, j) => {
	//
	// This algorithm reorganizes the input array `a` as follows
	//  - elements that are minima are put at the front of `a`
	//  - other elements are put at the back of `a`
	//
	// During the algorithm, `a` looks like this
	//
	//  ------------------------------------------------------
	// | minima set | candidate elements | discarded elements |
	//  ------------------------------------------------------
	//  i           min                dis                     j

	let min;
	let dis;
	let k;
	let inc;
	let temporary;

	min = i;
	dis = j - 1;

	// While there are candidate elements left.

	while (min <= dis) {
		// (1) Determine if the right-most candidate should be discarded because it
		// is dominated by one of the minima elements of the minima set in
		// construction.

		for (k = i; k < min && !prec(a[k], a[dis]); ++k);

		// If so, discard it.

		if (k < min) {
			--dis;
		}

		// (2) Otherwise, scan the candidates for a minimum. If at this point the
		// candidate set is not empty, at least one of its elements must be a
		// minimum. We scan the candidate list to find such a minimum.
		else {
			// Store the current minimum as the left-most candidate.

			temporary = a[dis];
			a[dis] = a[min];
			a[min] = temporary;

			// For each other candidate, right-to-left.

			for (inc = min + 1; inc <= dis; ) {
				// If the current minimum precedes the right-most candidate,
				// discard the right-most candidate.

				if (prec(a[min], a[dis])) {
					--dis;
				}

				// Else, if the right-most candidate precedes the current
				// minimum, we can discard the current minimum and the
				// right-most candidate becomes the current minimum.
				else if (prec(a[dis], a[min])) {
					temporary = a[dis];
					a[dis] = a[min];
					a[min] = temporary;
					--dis;
				}

				// Otherwise, we save the candidate for the next round.
				else {
					temporary = a[dis];
					a[dis] = a[inc];
					a[inc] = temporary;
					++inc;
				}
			}

			// The above loop selects a new minimum from the set of candidates
			// and places it at position `min`. We now increase the `min`
			// counter to move this minimum from the candidate list to the
			// minima set.

			++min;
		}
	}

	// The algorithm returns the outer right bound of the minima set a[i:min].

	return min;
};

export default clarkson;