Я пытаюсь написать функцию, которая делает следующее:
- принимает в качестве аргумента массив целых чисел (например, [1,2,3,4])
- создает массив всех возможных перестановок [1,2,3,4], причем каждая перестановка имеет длину 4
функция ниже (я нашел ее в Интернете) делает это, принимая строку в качестве аргумента и возвращая все перестановки этой строки
Я не мог понять, как изменить его, чтобы он работал с массивом целых чисел (я думаю, это как-то связано с тем, как некоторые методы работают со строками иначе, чем с целыми числами, но я не уверен. ..)
var permArr = [], usedChars = [];
function permute(input) {
var i, ch, chars = input.split("");
for (i = 0; i < chars.length; i++) {
ch = chars.splice(i, 1);
usedChars.push(ch);
if (chars.length == 0)
permArr[permArr.length] = usedChars.join("");
permute(chars.join(""));
chars.splice(i, 0, ch);
usedChars.pop();
}
return permArr
};
Примечание: я хочу, чтобы функция возвращала массивы целых чисел , а не массив строк .
Мне действительно нужно, чтобы решение было на JavaScript. Я уже понял, как это сделать на питоне
javascript
permutation
перец
источник
источник
Немного поздно, но хотелось бы добавить сюда более элегантную версию. Может быть любым массивом ...
function permutator(inputArr) { var results = []; function permute(arr, memo) { var cur, memo = memo || []; for (var i = 0; i < arr.length; i++) { cur = arr.splice(i, 1); if (arr.length === 0) { results.push(memo.concat(cur)); } permute(arr.slice(), memo.concat(cur)); arr.splice(i, 0, cur[0]); } return results; } return permute(inputArr); }
Добавление версии ES6 (2015). Также не изменяет исходный входной массив. Работает в консоли в Chrome ...
const permutator = (inputArr) => { let result = []; const permute = (arr, m = []) => { if (arr.length === 0) { result.push(m) } else { for (let i = 0; i < arr.length; i++) { let curr = arr.slice(); let next = curr.splice(i, 1); permute(curr.slice(), m.concat(next)) } } } permute(inputArr) return result; }
Так...
permutator(['c','a','t']);
Урожайность ...
[ [ 'c', 'a', 't' ], [ 'c', 't', 'a' ], [ 'a', 'c', 't' ], [ 'a', 't', 'c' ], [ 't', 'c', 'a' ], [ 't', 'a', 'c' ] ]
А также...
permutator([1,2,3]);
Урожайность ...
[ [ 1, 2, 3 ], [ 1, 3, 2 ], [ 2, 1, 3 ], [ 2, 3, 1 ], [ 3, 1, 2 ], [ 3, 2, 1 ] ]
источник
var results = new Array(factorial(inputArr.length)), length=0
, а затем заменивresults.push(…)
наresults[length++]=…
var cur, memo = memo || [];
?slice()
вpermute(curr.slice(), m.concat(next))
самом деле нужно?Следующий очень эффективный алгоритм использует метод Heap для генерации всех перестановок из N элементов со сложностью выполнения в O (N!):
function permute(permutation) { var length = permutation.length, result = [permutation.slice()], c = new Array(length).fill(0), i = 1, k, p; while (i < length) { if (c[i] < i) { k = i % 2 && c[i]; p = permutation[i]; permutation[i] = permutation[k]; permutation[k] = p; ++c[i]; i = 1; result.push(permutation.slice()); } else { c[i] = 0; ++i; } } return result; } console.log(permute([1, 2, 3]));
Тот же алгоритм реализован в виде генератора с пространственной сложностью в O (N):
Показать фрагмент кода
function* permute(permutation) { var length = permutation.length, c = Array(length).fill(0), i = 1, k, p; yield permutation.slice(); while (i < length) { if (c[i] < i) { k = i % 2 && c[i]; p = permutation[i]; permutation[i] = permutation[k]; permutation[k] = p; ++c[i]; i = 1; yield permutation.slice(); } else { c[i] = 0; ++i; } } } // Memory efficient iteration through permutations: for (var permutation of permute([1, 2, 3])) console.log(permutation); // Simple array conversion: var permutations = [...permute([1, 2, 3])];
Сравнение производительности
Не стесняйтесь добавлять свою реализацию в следующий набор тестов benchmark.js :
Показать фрагмент кода
function permute_SiGanteng(input) { var permArr = [], usedChars = []; function permute(input) { var i, ch; for (i = 0; i < input.length; i++) { ch = input.splice(i, 1)[0]; usedChars.push(ch); if (input.length == 0) { permArr.push(usedChars.slice()); } permute(input); input.splice(i, 0, ch); usedChars.pop(); } return permArr } return permute(input); } function permute_delimited(inputArr) { var results = []; function permute(arr, memo) { var cur, memo = memo || []; for (var i = 0; i < arr.length; i++) { cur = arr.splice(i, 1); if (arr.length === 0) { results.push(memo.concat(cur)); } permute(arr.slice(), memo.concat(cur)); arr.splice(i, 0, cur[0]); } return results; } return permute(inputArr); } function permute_monkey(inputArray) { return inputArray.reduce(function permute(res, item, key, arr) { return res.concat(arr.length > 1 && arr.slice(0, key).concat(arr.slice(key + 1)).reduce(permute, []).map(function(perm) { return [item].concat(perm); }) || item); }, []); } function permute_Oriol(input) { var permArr = [], usedChars = []; return (function main() { for (var i = 0; i < input.length; i++) { var ch = input.splice(i, 1)[0]; usedChars.push(ch); if (input.length == 0) { permArr.push(usedChars.slice()); } main(); input.splice(i, 0, ch); usedChars.pop(); } return permArr; })(); } function permute_MarkusT(input) { function permutate(array, callback) { function p(array, index, callback) { function swap(a, i1, i2) { var t = a[i1]; a[i1] = a[i2]; a[i2] = t; } if (index == array.length - 1) { callback(array); return 1; } else { var count = p(array, index + 1, callback); for (var i = index + 1; i < array.length; i++) { swap(array, i, index); count += p(array, index + 1, callback); swap(array, i, index); } return count; } } if (!array || array.length == 0) { return 0; } return p(array, 0, callback); } var result = []; permutate(input, function(a) { result.push(a.slice(0)); }); return result; } function permute_le_m(permutation) { var length = permutation.length, result = [permutation.slice()], c = new Array(length).fill(0), i = 1, k, p; while (i < length) { if (c[i] < i) { k = i % 2 && c[i], p = permutation[i]; permutation[i] = permutation[k]; permutation[k] = p; ++c[i]; i = 1; result.push(permutation.slice()); } else { c[i] = 0; ++i; } } return result; } function permute_Urielzen(arr) { var finalArr = []; var iterator = function (arrayTaken, tree) { for (var i = 0; i < tree; i++) { var temp = arrayTaken.slice(); temp.splice(tree - 1 - i, 0, temp.splice(tree - 1, 1)[0]); if (tree >= arr.length) { finalArr.push(temp); } else { iterator(temp, tree + 1); } } } iterator(arr, 1); return finalArr; } function permute_Taylor_Hakes(arr) { var permutations = []; if (arr.length === 1) { return [ arr ]; } for (var i = 0; i < arr.length; i++) { var subPerms = permute_Taylor_Hakes(arr.slice(0, i).concat(arr.slice(i + 1))); for (var j = 0; j < subPerms.length; j++) { subPerms[j].unshift(arr[i]); permutations.push(subPerms[j]); } } return permutations; } var Combinatorics = (function () { 'use strict'; var version = "0.5.2"; /* combinatory arithmetics */ var P = function(m, n) { var p = 1; while (n--) p *= m--; return p; }; var C = function(m, n) { if (n > m) { return 0; } return P(m, n) / P(n, n); }; var factorial = function(n) { return P(n, n); }; var factoradic = function(n, d) { var f = 1; if (!d) { for (d = 1; f < n; f *= ++d); if (f > n) f /= d--; } else { f = factorial(d); } var result = [0]; for (; d; f /= d--) { result[d] = Math.floor(n / f); n %= f; } return result; }; /* common methods */ var addProperties = function(dst, src) { Object.keys(src).forEach(function(p) { Object.defineProperty(dst, p, { value: src[p], configurable: p == 'next' }); }); }; var hideProperty = function(o, p) { Object.defineProperty(o, p, { writable: true }); }; var toArray = function(f) { var e, result = []; this.init(); while (e = this.next()) result.push(f ? f(e) : e); this.init(); return result; }; var common = { toArray: toArray, map: toArray, forEach: function(f) { var e; this.init(); while (e = this.next()) f(e); this.init(); }, filter: function(f) { var e, result = []; this.init(); while (e = this.next()) if (f(e)) result.push(e); this.init(); return result; }, lazyMap: function(f) { this._lazyMap = f; return this; }, lazyFilter: function(f) { Object.defineProperty(this, 'next', { writable: true }); if (typeof f !== 'function') { this.next = this._next; } else { if (typeof (this._next) !== 'function') { this._next = this.next; } var _next = this._next.bind(this); this.next = (function() { var e; while (e = _next()) { if (f(e)) return e; } return e; }).bind(this); } Object.defineProperty(this, 'next', { writable: false }); return this; } }; /* power set */ var power = function(ary, fun) { var size = 1 << ary.length, sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'index'); addProperties(that, { valueOf: sizeOf, init: function() { that.index = 0; }, nth: function(n) { if (n >= size) return; var i = 0, result = []; for (; n; n >>>= 1, i++) if (n & 1) result.push(this[i]); return (typeof (that._lazyMap) === 'function')?that._lazyMap(result):result; }, next: function() { return this.nth(this.index++); } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; /* combination */ var nextIndex = function(n) { var smallest = n & -n, ripple = n + smallest, new_smallest = ripple & -ripple, ones = ((new_smallest / smallest) >> 1) - 1; return ripple | ones; }; var combination = function(ary, nelem, fun) { if (!nelem) nelem = ary.length; if (nelem < 1) throw new RangeError; if (nelem > ary.length) throw new RangeError; var first = (1 << nelem) - 1, size = C(ary.length, nelem), maxIndex = 1 << ary.length, sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'index'); addProperties(that, { valueOf: sizeOf, init: function() { this.index = first; }, next: function() { if (this.index >= maxIndex) return; var i = 0, n = this.index, result = []; for (; n; n >>>= 1, i++) { if (n & 1) result[result.length] = this[i]; } this.index = nextIndex(this.index); return (typeof (that._lazyMap) === 'function')?that._lazyMap(result):result; } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; /* permutation */ var _permutation = function(ary) { var that = ary.slice(), size = factorial(that.length); that.index = 0; that.next = function() { if (this.index >= size) return; var copy = this.slice(), digits = factoradic(this.index, this.length), result = [], i = this.length - 1; for (; i >= 0; --i) result.push(copy.splice(digits[i], 1)[0]); this.index++; return (typeof (that._lazyMap) === 'function')?that._lazyMap(result):result; }; return that; }; // which is really a permutation of combination var permutation = function(ary, nelem, fun) { if (!nelem) nelem = ary.length; if (nelem < 1) throw new RangeError; if (nelem > ary.length) throw new RangeError; var size = P(ary.length, nelem), sizeOf = function() { return size; }, that = Object.create(ary.slice(), { length: { get: sizeOf } }); hideProperty(that, 'cmb'); hideProperty(that, 'per'); addProperties(that, { valueOf: function() { return size; }, init: function() { this.cmb = combination(ary, nelem); this.per = _permutation(this.cmb.next()); }, next: function() { var result = this.per.next(); if (!result) { var cmb = this.cmb.next(); if (!cmb) return; this.per = _permutation(cmb); return this.next(); } return (typeof (that._lazyMap) === 'function')?that._lazyMap(result):result; } }); addProperties(that, common); that.init(); return (typeof (fun) === 'function') ? that.map(fun) : that; }; /* export */ var Combinatorics = Object.create(null); addProperties(Combinatorics, { C: C, P: P, factorial: factorial, factoradic: factoradic, permutation: permutation, }); return Combinatorics; })(); function permute_Technicalbloke(inputArray) { if (inputArray.length === 1) return inputArray; return inputArray.reduce( function(accumulator,_,index){ permute_Technicalbloke([...inputArray.slice(0,index),...inputArray.slice(index+1)]) .map(value=>accumulator.push([inputArray[index],value])); return accumulator; },[]); } var suite = new Benchmark.Suite; var input = [0, 1, 2, 3, 4]; suite.add('permute_SiGanteng', function() { permute_SiGanteng(input); }) .add('permute_delimited', function() { permute_delimited(input); }) .add('permute_monkey', function() { permute_monkey(input); }) .add('permute_Oriol', function() { permute_Oriol(input); }) .add('permute_MarkusT', function() { permute_MarkusT(input); }) .add('permute_le_m', function() { permute_le_m(input); }) .add('permute_Urielzen', function() { permute_Urielzen(input); }) .add('permute_Taylor_Hakes', function() { permute_Taylor_Hakes(input); }) .add('permute_Combinatorics', function() { Combinatorics.permutation(input).toArray(); }) .add('permute_Technicalbloke', function() { permute_Technicalbloke(input); }) .on('cycle', function(event) { console.log(String(event.target)); }) .on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')); }) .run({async: true});
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/platform/1.3.4/platform.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/2.1.4/benchmark.min.js"></script>
Результаты выполнения Chrome 48:
источник
yield permutation.slice()
если вы не разрезаете, вы получаете только последнюю вычисленную перестановку.var inputArray = [1, 2, 3]; var result = inputArray.reduce(function permute(res, item, key, arr) { return res.concat(arr.length > 1 && arr.slice(0, key).concat(arr.slice(key + 1)).reduce(permute, []).map(function(perm) { return [item].concat(perm); }) || item); }, []); alert(JSON.stringify(result));
источник
.filter(uniq)
по результату.[1,2,3].length == 3 && "foo" || "bar"
или[1,2].length == 3 && "foo" || "bar"
о боже! Там есть!(or (and (= 3 2) (print "hello!")) (print "goodbye"))
Я улучшил SiGanteng «s ответ .
Теперь можно звонить
permute
более одного раза, потому чтоpermArr
иusedChars
каждый раз сбрасываются.function permute(input) { var permArr = [], usedChars = []; return (function main() { for (var i = 0; i < input.length; i++) { var ch = input.splice(i, 1)[0]; usedChars.push(ch); if (input.length == 0) { permArr.push(usedChars.slice()); } main(); input.splice(i, 0, ch); usedChars.pop(); } return permArr; })(); }
Показать фрагмент кода
function permute(input) { var permArr = [], usedChars = []; return (function main() { for (var i = 0; i < input.length; i++) { var ch = input.splice(i, 1)[0]; usedChars.push(ch); if (input.length == 0) { permArr.push(usedChars.slice()); } main(); input.splice(i, 0, ch); usedChars.pop(); } return permArr; })(); } document.write(JSON.stringify(permute([5, 3, 7, 1])));
источник
В большинстве ответов на этот вопрос используются дорогостоящие операции, такие как непрерывная вставка и удаление элементов в массиве или повторное копирование массивов.
Вместо этого это типичное решение для поиска с возвратом:
function permute(arr) { var results = [], l = arr.length, used = Array(l), // Array of bools. Keeps track of used items data = Array(l); // Stores items of the current permutation (function backtracking(pos) { if(pos == l) return results.push(data.slice()); for(var i=0; i<l; ++i) if(!used[i]) { // Iterate unused items used[i] = true; // Mark item as used data[pos] = arr[i]; // Assign item at the current position backtracking(pos+1); // Recursive call used[i] = false; // Mark item as not used } })(0); return results; }
permute([1,2,3,4]); // [ [1,2,3,4], [1,2,4,3], /* ... , */ [4,3,2,1] ]
Поскольку массив результатов будет огромным, было бы неплохо перебирать результаты один за другим вместо того, чтобы распределять все данные одновременно. В ES6 это можно сделать с помощью генераторов:
function permute(arr) { var l = arr.length, used = Array(l), data = Array(l); return function* backtracking(pos) { if(pos == l) yield data.slice(); else for(var i=0; i<l; ++i) if(!used[i]) { used[i] = true; data[pos] = arr[i]; yield* backtracking(pos+1); used[i] = false; } }(0); }
var p = permute([1,2,3,4]); p.next(); // {value: [1,2,3,4], done: false} p.next(); // {value: [1,2,4,3], done: false} // ... p.next(); // {value: [4,3,2,1], done: false} p.next(); // {value: undefined, done: true}
источник
Следующая функция переставляет массив любого типа и вызывает указанную функцию обратного вызова для каждой найденной перестановки:
/* Permutate the elements in the specified array by swapping them in-place and calling the specified callback function on the array for each permutation. Return the number of permutations. If array is undefined, null or empty, return 0. NOTE: when permutation succeeds, the array should be in the original state on exit! */ function permutate(array, callback) { // Do the actual permuation work on array[], starting at index function p(array, index, callback) { // Swap elements i1 and i2 in array a[] function swap(a, i1, i2) { var t = a[i1]; a[i1] = a[i2]; a[i2] = t; } if (index == array.length - 1) { callback(array); return 1; } else { var count = p(array, index + 1, callback); for (var i = index + 1; i < array.length; i++) { swap(array, i, index); count += p(array, index + 1, callback); swap(array, i, index); } return count; } } if (!array || array.length == 0) { return 0; } return p(array, 0, callback); }
Если вы назовете это так:
// Empty array to hold results var result = []; // Permutate [1, 2, 3], pushing every permutation onto result[] permutate([1, 2, 3], function (a) { // Create a copy of a[] and add that to result[] result.push(a.slice(0)); }); // Show result[] document.write(result);
Я думаю, он сделает именно то, что вам нужно - заполнит массив, называемый
result
перестановками массива [1, 2, 3]. Результат:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,2,1],[3,1,2]]
Чуть более понятный код на JSFiddle: http://jsfiddle.net/MgmMg/6/
источник
Некоторая версия, вдохновленная Haskell:
function perms(xs) { if (!xs.length) return [[]]; return xs.flatMap(x => { // get permutations of xs without x, then prepend x to each return perms(xs.filter(v => v!==x)).map(vs => [x, ...vs]); }); } document.write(JSON.stringify(perms([1,2,3])));
источник
Это интересная задача, и вот мой вклад. Это очень просто и быстро. Если интересно, пожалуйста, подождите и продолжайте читать.
Если вы хотите быстро выполнить эту работу, вам определенно нужно заняться динамическим программированием. Это означает, что вам следует забыть о рекурсивных подходах. Это уж точно...
Хорошо, что код le_m, использующий метод Heap, пока что кажется самым быстрым. Ну, у меня нет названия для моего алгоритма, я не знаю, реализован он уже или нет, но он очень простой и быстрый. Как и все подходы к динамическому программированию, мы начнем с простейшей задачи и перейдем к конечному результату.
Предполагая, что у нас есть массив,
a = [1,2,3]
мы начнем сr = [[1]]; // result t = []; // interim result
Затем выполните эти три шага;
r
(результирующего) массива мы добавим следующий элемент входного массива.t
. (ну кроме первого, чтобы не терять время на 0 оборотов)r
промежуточного массива,t
должен сохраниться следующий уровень результатов, поэтому мы делаемr = t; t = [];
и продолжаем до длины входного массиваa
.Итак, следующие наши шаги;
r array | push next item to | get length many rotations | each sub array | of each subarray ----------------------------------------------------------- [[1]] | [[1,2]] | [[1,2],[2,1]] ----------|-------------------|---------------------------- [[1,2], | [[1,2,3], | [[1,2,3],[2,3,1],[3,1,2], [2,1]] | [2,1,3]] | [2,1,3],[1,3,2],[3,2,1]] ----------|-------------------|---------------------------- previous t| | -----------------------------------------------------------
Итак, вот код
function perm(a){ var r = [[a[0]]], t = [], s = []; if (a.length <= 1) return a; for (var i = 1, la = a.length; i < la; i++){ for (var j = 0, lr = r.length; j < lr; j++){ r[j].push(a[i]); t.push(r[j]); for(var k = 1, lrj = r[j].length; k < lrj; k++){ for (var l = 0; l < lrj; l++) s[l] = r[j][(k+l)%lrj]; t[t.length] = s; s = []; } } r = t; t = []; } return r; } var arr = [0,1,2,4,5]; console.log("The length of the permutation is:",perm(arr).length); console.time("Permutation test"); for (var z = 0; z < 2000; z++) perm(arr); console.timeEnd("Permutation test");
В нескольких тестах я видел, что он разрешает 120 перестановок [0,1,2,3,4] 2000 раз за 25 ~ 35 мс.
источник
rotatePerm
(выше) всегда на 1,2 быстрее. С FF нет согласованности. После нескольких тестов иногдаheapPerm
в 2 раза быстрее, иногдаrotatePerm
в 1,1 раза быстрее. С другими веб-браузерами, такими как Opera или Epiphany,rotatePerm
стабильно оказывается в 1,1 раза быстрее. Однако с EdgeheapPerm
каждый раз он работает в 1,2 раза быстрее.permutations
функция :)Самая быстрая, наиболее эффективная и элегантная версия на сегодняшний день (2020)
function getArrayMutations (arr, perms = [], len = arr.length) { if (len === 1) perms.push(arr.slice(0)) for (let i = 0; i < len; i++) { getArrayMutations(arr, perms, len - 1) len % 2 // parity dependent adjacent elements swap ? [arr[0], arr[len - 1]] = [arr[len - 1], arr[0]] : [arr[i], arr[len - 1]] = [arr[len - 1], arr[i]] } return perms } const arrayToMutate = [1, 2, 3, 4, 5, 6, 7, 8, 9] const startTime = performance.now() const arrayOfMutations = getArrayMutations(arrayToMutate) const stopTime = performance.now() const duration = (stopTime - startTime) / 1000 console.log(`${arrayOfMutations.length.toLocaleString('en-US')} permutations found in ${duration.toLocaleString('en-US')}s`)
источник
len % 2 // parity dependent adjacent elements swap
средство и для чего оно используется?Ответить без необходимости использования внешнего массива или дополнительной функции
function permutator (arr) { var permutations = []; if (arr.length === 1) { return [ arr ]; } for (var i = 0; i < arr.length; i++) { var subPerms = permutator(arr.slice(0, i).concat(arr.slice(i + 1))); for (var j = 0; j < subPerms.length; j++) { subPerms[j].unshift(arr[i]); permutations.push(subPerms[j]); } } return permutations; }
источник
Вот классное решение
const rotations = ([l, ...ls], right=[]) => l ? [[l, ...ls, ...right], ...rotations(ls, [...right, l])] : [] const permutations = ([x, ...xs]) => x ? permutations(xs).flatMap((p) => rotations([x, ...p])) : [[]] console.log(permutations("cat"))
источник
Вот еще одно «более рекурсивное» решение.
function perms(input) { var data = input.slice(); var permutations = []; var n = data.length; if (n === 0) { return [ [] ]; } else { var first = data.shift(); var words = perms(data); words.forEach(function(word) { for (var i = 0; i < n; ++i) { var tmp = word.slice(); tmp.splice(i, 0, first) permutations.push(tmp); } }); } return permutations; } var str = 'ABC'; var chars = str.split(''); var result = perms(chars).map(function(p) { return p.join(''); }); console.log(result);
Выход:
[ 'ABC', 'BAC', 'BCA', 'ACB', 'CAB', 'CBA' ]
источник
function perm(xs) { return xs.length === 0 ? [[]] : perm(xs.slice(1)).reduce(function (acc, ys) { for (var i = 0; i < xs.length; i++) { acc.push([].concat(ys.slice(0, i), xs[0], ys.slice(i))); } return acc; }, []); }
Проверьте это с помощью:
console.log(JSON.stringify(perm([1, 2, 3,4])));
источник
В большинстве других ответов не используются новые функции генератора javascript, что является идеальным решением проблемы такого типа. Возможно, вам понадобится только одна перестановка в памяти за раз. Кроме того, я предпочитаю генерировать перестановку диапазона индексов, поскольку это позволяет мне индексировать каждую перестановку и переходить прямо к любой конкретной перестановке, а также использоваться для перестановки любой другой коллекции.
// ES6 generator version of python itertools [permutations and combinations] const range = function*(l) { for (let i = 0; i < l; i+=1) yield i; } const isEmpty = arr => arr.length === 0; const permutations = function*(a) { const r = arguments[1] || []; if (isEmpty(a)) yield r; for (let i of range(a.length)) { const aa = [...a]; const rr = [...r, ...aa.splice(i, 1)]; yield* permutations(aa, rr); } } console.log('permutations of ABC'); console.log(JSON.stringify([...permutations([...'ABC'])])); const combinations = function*(a, count) { const r = arguments[2] || []; if (count) { count = count - 1; for (let i of range(a.length - count)) { const aa = a.slice(i); const rr = [...r, ...aa.splice(0, 1)]; yield* combinations(aa, count, rr); } } else { yield r; } } console.log('combinations of 2 of ABC'); console.log(JSON.stringify([...combinations([...'ABC'], 2)])); const permutator = function() { const range = function*(args) { let {begin = 0, count} = args; for (let i = begin; count; count--, i+=1) { yield i; } } const factorial = fact => fact ? fact * factorial(fact - 1) : 1; return { perm: function(n, permutationId) { const indexCount = factorial(n); permutationId = ((permutationId%indexCount)+indexCount)%indexCount; let permutation = [0]; for (const choiceCount of range({begin: 2, count: n-1})) { const choice = permutationId % choiceCount; const lastIndex = permutation.length; permutation.push(choice); permutation = permutation.map((cv, i, orig) => (cv < choice || i == lastIndex) ? cv : cv + 1 ); permutationId = Math.floor(permutationId / choiceCount); } return permutation.reverse(); }, perms: function*(n) { for (let i of range({count: factorial(n)})) { yield this.perm(n, i); } } }; }(); console.log('indexing type permutator'); let i = 0; for (let elem of permutator.perms(3)) { console.log(`${i}: ${elem}`); i+=1; } console.log(); console.log(`3: ${permutator.perm(3,3)}`);
источник
#!/usr/bin/env node "use strict"; function perm(arr) { if(arr.length<2) return [arr]; var res = []; arr.forEach(function(x, i) { perm(arr.slice(0,i).concat(arr.slice(i+1))).forEach(function(a) { res.push([x].concat(a)); }); }); return res; } console.log(perm([1,2,3,4]));
источник
Вот тот, который я сделал ...
const permute = (ar) => ar.length === 1 ? ar : ar.reduce( (ac,_,i) => {permute([...ar.slice(0,i),...ar.slice(i+1)]).map(v=>ac.push([].concat(ar[i],v))); return ac;},[]);
И вот оно опять, но менее лаконично! ...
function permute(inputArray) { if (inputArray.length === 1) return inputArray; return inputArray.reduce( function(accumulator,_,index){ permute([...inputArray.slice(0,index),...inputArray.slice(index+1)]) .map(value=>accumulator.push([].concat(inputArray[index],value))); return accumulator; },[]); }
Как это работает: если массив длиннее одного элемента, он проходит через каждый элемент и объединяет его с рекурсивным вызовом самого себя с оставшимися элементами в качестве аргумента. Он не изменяет исходный массив.
источник
Функциональный ответ с использованием flatMap:
const getPermutationsFor = (arr, permutation = []) => arr.length === 0 ? [permutation] : arr.flatMap((item, i, arr) => getPermutationsFor( arr.filter((_,j) => j !== i), [...permutation, item] ) );
источник
Мой первый вклад на сайт. Кроме того, согласно тестам, которые я сделал, этот код работает быстрее, чем все другие методы, упомянутые здесь до этой даты, конечно, он минимален, если есть несколько значений, но время увеличивается экспоненциально при добавлении слишком большого количества.
function permutations(arr) { var finalArr = []; function iterator(arrayTaken, tree) { var temp; for (var i = 0; i < tree; i++) { temp = arrayTaken.slice(); temp.splice(tree - 1 - i, 0, temp.splice(tree - 1, 1)[0]); if (tree >= arr.length) { finalArr.push(temp); } else { iterator(temp, tree + 1); } } } iterator(arr, 1); return finalArr; };
источник
"use strict"; function getPermutations(arrP) { var results = []; var arr = arrP; arr.unshift(null); var length = arr.length; while (arr[0] === null) { results.push(arr.slice(1).join('')); let less = null; let lessIndex = null; for (let i = length - 1; i > 0; i--) { if(arr[i - 1] < arr[i]){ less = arr[i - 1]; lessIndex = i - 1; break; } } for (let i = length - 1; i > lessIndex; i--) { if(arr[i] > less){ arr[lessIndex] = arr[i]; arr[i] = less; break; } } for(let i = lessIndex + 1; i<length; i++){ for(let j = i + 1; j < length; j++){ if(arr[i] > arr[j] ){ arr[i] = arr[i] + arr[j]; arr[j] = arr[i] - arr[j]; arr[i] = arr[i] - arr[j]; } } } } return results; } var res = getPermutations([1,2,3,4,5]); var out = document.getElementById('myTxtArr'); res.forEach(function(i){ out.value+=i+', '});
textarea{ height:500px; width:500px; }
<textarea id='myTxtArr'></textarea>
Выводит лексикографически упорядоченные перестановки. Работает только с числами. В противном случае вам нужно изменить метод подкачки в строке 34.
источник
По духу похоже на решение в стиле Haskell от @crl, но работает с
reduce
:function permutations( base ) { if (base.length == 0) return [[]] return permutations( base.slice(1) ).reduce( function(acc,perm) { return acc.concat( base.map( function(e,pos) { var new_perm = perm.slice() new_perm.splice(pos,0,base[0]) return new_perm })) },[]) }
источник
Это очень хороший вариант использования map / reduce:
function permutations(arr) { return (arr.length === 1) ? arr : arr.reduce((acc, cv, index) => { let remaining = [...arr]; remaining.splice(index, 1); return acc.concat(permutations(remaining).map(a => [].concat(cv,a))); }, []); }
[].concat(cv,a)
источник
Вот минимальная версия ES6. Сглаженный и без функций можно вытащить из Lodash.
const flatten = xs => xs.reduce((cum, next) => [...cum, ...next], []); const without = (xs, x) => xs.filter(y => y !== x); const permutations = xs => flatten(xs.map(x => xs.length < 2 ? [xs] : permutations(without(xs, x)).map(perm => [x, ...perm]) ));
Результат:
permutations([1,2,3]) // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
источник
perm = x => x[0] ? x.reduce((a, n) => (perm(x.filter(m => m!=n)).forEach(y => a.push([n,...y])), a), []): [[]]
источник
const permutations = array => { let permut = []; helperFunction(0, array, permut); return permut; }; const helperFunction = (i, array, permut) => { if (i === array.length - 1) { permut.push(array.slice()); } else { for (let j = i; j < array.length; j++) { swapElements(i, j, array); helperFunction(i + 1, array, permut); swapElements(i, j, array); } } }; function swapElements(a, b, array) { let temp = array[a]; array[a] = array[b]; array[b] = temp; } console.log(permutations([1, 2, 3]));
источник
Довольно поздно. Еще на всякий случай, если это кому-то поможет.
function permute(arr) { if (arr.length == 1) return arr let res = arr.map((d, i) => permute([...arr.slice(0, i),...arr.slice(i + 1)]) .map(v => [d,v].join(''))).flat() return res } console.log(permute([1,2,3,4]))
источник
У меня была трещина в создании версии этого, которая пытается быть краткой, но удобочитаемой и чисто функциональной.
function stringPermutations ([...input]) { if (input.length === 1) return input; return input .map((thisChar, index) => { const remainingChars = [...input.slice(0, index), ...input.slice(index + 1)]; return stringPermutations(remainingChars) .map(remainder => thisChar + remainder); }) .reduce((acc, cur) => [...acc, ...cur]); }
Обратите внимание, что форматирование аргументов превращает входную строку в массив. Не уверен, что это слишком волшебно ... Не уверен, что видел это в дикой природе. Для реальной читаемости я бы, вероятно, сделал
input = [...input]
для первой строки функции.источник
Это реализация алгоритма Heap (похожего на алгоритм @ le_m), за исключением того, что он рекурсивный.
function permute_kingzee(arr,n=arr.length,out=[]) { if(n == 1) { return out.push(arr.slice()); } else { for(let i=0; i<n; i++) { permute_kingzee(arr,n-1, out); let j = ( n % 2 == 0 ) ? i : 0; let t = arr[n-1]; arr[n-1] = arr[j]; arr[j] = t; } return out; } }
Похоже, это тоже намного быстрее: https://jsfiddle.net/3brqzaLe/
источник
Я написал сообщение, чтобы продемонстрировать, как переставлять массив в JavaScript. Вот код, который это делает.
var count=0; function permute(pre,cur){ var len=cur.length; for(var i=0;i<len;i++){ var p=clone(pre); var c=clone(cur); p.push(cur[i]); remove(c,cur[i]); if(len>1){ permute(p,c); }else{ print(p); count++; } } } function print(arr){ var len=arr.length; for(var i=0;i<len;i++){ document.write(arr[i]+" "); } document.write("<br />"); } function remove(arr,item){ if(contains(arr,item)){ var len=arr.length; for(var i = len-1; i >= 0; i--){ // STEP 1 if(arr[i] == item){ // STEP 2 arr.splice(i,1); // STEP 3 } } } } function contains(arr,value){ for(var i=0;i<arr.length;i++){ if(arr[i]==value){ return true; } } return false; } function clone(arr){ var a=new Array(); var len=arr.length; for(var i=0;i<len;i++){ a.push(arr[i]); } return a; }
Просто позвони
будет работать. Подробнее о том, как это работает, см. В объяснении в этой публикации.
источник
function nPr(xs, r) { if (!r) return []; return xs.reduce(function(memo, cur, i) { var others = xs.slice(0,i).concat(xs.slice(i+1)), perms = nPr(others, r-1), newElms = !perms.length ? [[cur]] : perms.map(function(perm) { return [cur].concat(perm) }); return memo.concat(newElms); }, []); }
источник