permutation-sjt
Version:
A quite fast non-recursive permutation algorithm, Steinhaus–Johnson–Trotter algorithm (Even's speedup)
134 lines (133 loc) • 4.95 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Permutation = void 0;
/**
* Steinhaus–Johnson–Trotter algorithm, Even's speedup
* https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm
*
*
* The Steinhaus–Johnson–Trotter algorithm or Johnson–Trotter algorithm generates
* all of the permutations of n elements. Each permutation in the sequence that
* it generates differs from the previous permutation by swapping two adjacent
* elements of the sequence.
*
* An improvement of the Steinhaus–Johnson–Trotter algorithm by Shimon Even
* provides an improvement to the running time
* of the algorithm by storing additional information for each element in the
* permutation: its position, and a direction (positive, negative, or zero) in which
* it is currently moving.
*/
var Permutation = /** @class */ (function () {
/**
*
* @param n numbers in the arrray 1..n
*/
function Permutation(n, start) {
if (start === void 0) { start = 0; }
this.n = n;
this.start = start;
this.numbers = [];
this.directions = [];
this.positions = [];
this.terminated = false;
if (n < 0)
throw new Error('Permutation constructor expects a positive number');
/**
* Initially, the direction of the number 1 is zero,
* and all other elements have a negative direction
*/
for (var i = 0; i < n; i++) {
this.numbers.push(i);
this.positions.push(i);
if (i === 0)
this.directions.push(0);
else
this.directions.push(-1);
}
}
/**
* Checks if there is a permutation to return.
*
* @returns
*/
Permutation.prototype.hasNext = function () {
return !this.terminated;
};
/**
* Returns the next permutation of the Steinhaus–Johnson–Trotter algorithm,
* starting with [1,2,3, ..., n].
*
* Returns undefined if the permutations have been exhausted.
*
* @returns the next permutation or undefined
*/
Permutation.prototype.next = function () {
var _this = this;
if (this.terminated) {
throw new Error('next(): there is no next permutation');
}
var copy = this.numbers.slice(0);
this.generateNext();
if (this.start > 0)
return copy.map(function (i) { return i + _this.start; });
else
return copy;
};
/**
* At each step, the algorithm finds the greatest element with a nonzero direction,
* and swaps it in the indicated direction.
*
* When all numbers become unmarked, the algorithm terminates.
*/
Permutation.prototype.generateNext = function () {
// findMaxWithDirection;
var index = -1;
for (var i = this.n - 1; i >= 0; i--) {
if (this.directions[i] !== 0) {
index = this.positions[i];
break;
}
}
// swap
if (index !== -1)
this.swapWithNextElementInDirection(index);
else
this.terminated = true;
};
Permutation.prototype.swapWithNextElementInDirection = function (index) {
var _a, _b;
// precondition this.directions[index] not 0
/**
* swaps it in the indicated direction
*/
var number = this.numbers[index];
var newIndex = index + this.directions[number];
var otherNumber = this.numbers[newIndex];
_a = [this.positions[otherNumber], this.positions[number]], this.positions[number] = _a[0], this.positions[otherNumber] = _a[1];
_b = [this.numbers[index], this.numbers[newIndex]], this.numbers[newIndex] = _b[0], this.numbers[index] = _b[1];
/**
* If this causes the chosen element to reach the first or last position
* within the permutation, or if the next element in the same direction
* is greater than the chosen element, the direction of the chosen element is set to zero:
*/
if (newIndex === 0 ||
newIndex === this.numbers.length - 1 ||
this.numbers[newIndex] < this.numbers[newIndex + this.directions[number]]) {
this.directions[number] = 0;
}
/**
* After each step, all elements greater than the chosen element
* (which previously had direction zero) have their directions
* set to indicate motion toward the chosen element.
* */
for (var i = 0; i < this.numbers.length; i++) {
if (i === newIndex)
continue;
if (this.numbers[i] > this.numbers[newIndex]) {
this.directions[this.numbers[i]] = i < newIndex ? 1 : -1;
}
}
};
return Permutation;
}());
exports.Permutation = Permutation;