Переписать код Python в код JavaScript
Имеется Python код, который нужно переписать под JavaScript
Код:
import itertools
x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36]
y = set()
z = 6
for x in itertools.combinations(x,z):
y.add(x)
print(y)
Данный код передаёт в переменную 'y' все возможные уникальные комбинации элементов массива 'x'. Длина получаемых комбинаций равна 'z'
Пример результата:
{(3, 5, 6, 7, 8, 9), (1, 3, 5, 7, 8, 9), (1, 4, 5, 7, 8, 10), ..., ...}
Нашёл порт библиотеки itertools на JS, однако в нём нет необходимого мне метода itertools.combinations(), однако присутствует intertools.permutations(), который мне не подходит.
Ответы (3 шт):
Это исходная функция на Python:
def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool = tuple(iterable)
n = len(pool)
if r > n:
return
indices = list(range(r))
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != i + n - r:
break
else:
return
indices[i] += 1
for j in range(i+1, r):
indices[j] = indices[j-1] + 1
yield tuple(pool[i] for i in indices)
А это моя попытка перевести в JavaScript:
function combinations(iterable, r){
let arr = [...iterable];
let len = arr.length;
if(r > len) return;
let combs = [];
findCombs(arr,r);
function findCombs(arr,n,last=0,comb=[]){
for(let i=last; i<len; i++){
let cur = [...comb,arr[i]];
if(cur.length == n){
combs.push(cur);
continue;
}
findCombs(arr,n,i+1,cur);
}
}
return combs;
}
console.log(combinations([1,2,3,4,5,6,7,8,9,10,11,12],6));
Вычисление комбинаций дело трудоемкое, размер изначального массива я сократил.
Имея готовый алгоритм для перестановок типа next_permutation, сочетания получить нетрудно.
Создайте массив длиной n, содержащий n-k нулей и k единиц.
Запустите на нём генерацию перестановок.
Для каждой перестановки используйте те элементы из вашего начального массива, на чьих индексах в перестановке стоят единицы. В вашем случае элементы массива - просто числа от 1 до n, поэтому при обработке перестановки, встретив на i-м месте единицу, записывайте в сочетание число i+1.
Таким образом, перестановке [0,1,1,0] соответствует комбинация [2,3]
Edit
Как подсказал Stanislav Volodarskiy, в itertools.js генератор перестановок использует перестановку индексов, так что на массиве с неуникальными элементами работать будет неоптимально.
Вот код получения следующей в лексикографическом порядке перестановки, основанный на сравнениях самих элементов, взятый отсюда (там есть ещё версия с компаратором). Впрочем, код генерации непосредственно сочетаний уже дали, так что приведённая схема полезна только в контексте общего образования.
/*
* Next lexicographical permutation algorithm (JavaScript)
* by Project Nayuki, 2018. Public domain.
* https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
*/
"use strict";
/* Basic version */
/*
* Computes the next lexicographical permutation of the specified array of numbers in place,
* returning whether a next permutation existed. (Returns false when the argument
* is already the last possible permutation.)
*/
function nextPermutation(array) {
// Find non-increasing suffix
var i = array.length - 1;
while (i > 0 && array[i - 1] >= array[i])
i--;
if (i <= 0)
return false;
// Find successor to pivot
var j = array.length - 1;
while (array[j] <= array[i - 1])
j--;
var temp = array[i - 1];
array[i - 1] = array[j];
array[j] = temp;
// Reverse suffix
j = array.length - 1;
while (i < j) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
return true;
}
Генератор комбинаций combinations порождает комбинации в лексикографическом порядке. Для каждой комбинации вызывается функция cb.
Функция array_combs применяет генератор к массиву как в вашем вопросе.
const combinations = (n, k, cb) => {
console.assert(Number.isInteger(n));
console.assert(Number.isInteger(k));
console.assert(0 <= k);
console.assert(0 <= n);
if (k > n) {
return;
}
const a = [...Array(k).keys()];
cb(a);
outer:
while (true) {
for (let i = k - 1; i >= 0; --i) {
const b = a[i] + 1 - i;
if (b <= n - k) {
for (let j = i; j < k; ++j) {
a[j] = b + j;
}
cb(a);
continue outer;
}
}
break;
}
};
const array_combs = (a, k) => {
const combs = [];
combinations(a.length, k, s => {
combs.push(s.map(i => a[i]));
});
return combs;
};
console.log(array_combs([1, 2, 3, 4, 5, 6], 3))
[ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ], [ 1, 2, 6 ], [ 1, 3, 4 ], [ 1, 3, 5 ], [ 1, 3, 6 ], [ 1, 4, 5 ], [ 1, 4, 6 ], [ 1, 5, 6 ], [ 2, 3, 4 ], [ 2, 3, 5 ], [ 2, 3, 6 ], [ 2, 4, 5 ], [ 2, 4, 6 ], [ 2, 5, 6 ], [ 3, 4, 5 ], [ 3, 4, 6 ], [ 3, 5, 6 ], [ 4, 5, 6 ] ]