fix(permutations-heap): do the swaps on a copy of the input
Heap's Algorithm generates each permutation by swapping two elements of a working array in place, and that working array was the argument itself, so permutationsHeap([1, 2, 3]) left the caller holding [3, 2, 1]. Two calls on one array returned different lists, and a frozen array threw. Copy the input up front and swap on the copy, as shuffle and numericSort already do. The returned permutations are unchanged.
This commit is contained in:
1 parent
cd7bef4165
commit
5894b8b560
3 files changed
+35
-8
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"simple-statistics": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix `permutationsHeap` permuting the caller's array. Heap's Algorithm works by swapping elements in place, and the implementation ran those swaps directly on the argument, so `permutationsHeap([1, 2, 3])` left the caller holding `[3, 2, 1]`, the last permutation generated. Calling it twice on the same array returned two different lists, and a frozen input threw `TypeError: Cannot assign to read only property '0'`. The swaps now run on a copy, matching the defensive copying already used in `shuffle`. The returned permutations are unchanged.
|
||||||
@@ -2,18 +2,25 @@
|
|||||||
* Implementation of [Heap's Algorithm](https://en.wikipedia.org/wiki/Heap%27s_algorithm)
|
* Implementation of [Heap's Algorithm](https://en.wikipedia.org/wiki/Heap%27s_algorithm)
|
||||||
* for generating permutations.
|
* for generating permutations.
|
||||||
*
|
*
|
||||||
|
* Heap's Algorithm permutes an array in place. This function does the swapping
|
||||||
|
* on a copy, so it will not modify its input.
|
||||||
|
*
|
||||||
* @param {Array} elements any type of data
|
* @param {Array} elements any type of data
|
||||||
* @returns {Array<Array>} array of permutations
|
* @returns {Array<Array>} array of permutations
|
||||||
*/
|
*/
|
||||||
function permutationsHeap(elements) {
|
function permutationsHeap(elements) {
|
||||||
const indexes = new Array(elements.length);
|
// slice the original array so that it is not modified: the swaps below
|
||||||
const permutations = [elements.slice()];
|
// are done in place
|
||||||
|
const sample = elements.slice();
|
||||||
|
|
||||||
for (let i = 0; i < elements.length; i++) {
|
const indexes = new Array(sample.length);
|
||||||
|
const permutations = [sample.slice()];
|
||||||
|
|
||||||
|
for (let i = 0; i < sample.length; i++) {
|
||||||
indexes[i] = 0;
|
indexes[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < elements.length; ) {
|
for (let i = 0; i < sample.length; ) {
|
||||||
if (indexes[i] < i) {
|
if (indexes[i] < i) {
|
||||||
// At odd indexes, swap from indexes[i] instead
|
// At odd indexes, swap from indexes[i] instead
|
||||||
// of from the beginning of the array
|
// of from the beginning of the array
|
||||||
@@ -24,11 +31,11 @@ function permutationsHeap(elements) {
|
|||||||
|
|
||||||
// swap between swapFrom and i, using
|
// swap between swapFrom and i, using
|
||||||
// a temporary variable as storage.
|
// a temporary variable as storage.
|
||||||
const temp = elements[swapFrom];
|
const temp = sample[swapFrom];
|
||||||
elements[swapFrom] = elements[i];
|
sample[swapFrom] = sample[i];
|
||||||
elements[i] = temp;
|
sample[i] = temp;
|
||||||
|
|
||||||
permutations.push(elements.slice());
|
permutations.push(sample.slice());
|
||||||
indexes[i]++;
|
indexes[i]++;
|
||||||
i = 0;
|
i = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -16,4 +16,19 @@ describe("permutationsHeap", function () {
|
|||||||
[3, 2, 1]
|
[3, 2, 1]
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
it("does not modify the caller's array", function () {
|
||||||
|
const x = [1, 2, 3];
|
||||||
|
ss.permutationsHeap(x);
|
||||||
|
assert.deepEqual(x, [1, 2, 3]);
|
||||||
|
});
|
||||||
|
it("accepts a frozen array", function () {
|
||||||
|
assert.deepEqual(ss.permutationsHeap(Object.freeze([1, 2])), [
|
||||||
|
[1, 2],
|
||||||
|
[2, 1]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it("returns the same permutations when called twice on one array", function () {
|
||||||
|
const x = [1, 2, 3];
|
||||||
|
assert.deepEqual(ss.permutationsHeap(x), ss.permutationsHeap(x));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user