UNPKG

permutation-sjt

Version:

A quite fast non-recursive permutation algorithm, Steinhaus–Johnson–Trotter algorithm (Even's speedup)

134 lines (133 loc) 4.95 kB
"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;