Массив массивов - все перестановки [дубликаты]

Другая возможность - рассмотреть возможность создания чего-то вроде приложения ClickOnce на платформе Windows. Это предоставит ссылку на загружаемый исполняемый файл, который будет запускаться в ОС пользователя, но может сделать обратные вызовы в Интернете для отправки и получения данных. Приложение ClickOnce может встроить элемент управления браузером и, таким образом, у вас будет как веб-совместимое приложение, способное напрямую общаться с хранилищем пользователя.

42
задан Yahel 19 August 2013 в 01:14
поделиться

9 ответов

Это не перестановки, см. определения перестановок из Википедии.

Но вы можете добиться этого с помощью рекурсии:

var allArrays = [['a', 'b'], ['c'], ['d', 'e', 'f']]

function allPossibleCases(arr) {
  if (arr.length == 1) {
    return arr[0];
  } else {
    var result = [];
    var allCasesOfRest = allPossibleCases(arr.slice(1));  // recur with the rest of array
    for (var i = 0; i < allCasesOfRest.length; i++) {
      for (var j = 0; j < arr[0].length; j++) {
        result.push(arr[0][j] + allCasesOfRest[i]);
      }
    }
    return result;
  }

}

Вы также можете сделать это с петлями, но это будет немного сложно, и вам потребуется реализовать свой собственный аналог стека.

46
ответ дан ffriend 16 August 2018 в 12:02
поделиться
  • 1
    Я решил, что это потребует рекурсии. 2 младших синтаксических пункта: case - зарезервированное слово, и вам не хватает точки с запятой в первой строке. Я не уверен, что происходит в var allCasesofRest= ... – Yahel 2 December 2010 в 03:55
  • 2
    Вы имели в виду `var allCasesOfRest = allPossibleCases (arr.slice (1));` – Yahel 2 December 2010 в 04:01
  • 3
    Вы правы, исправили это. И да, будет рекурсия, но если вы поняли, что можете переписать ее для использования итерации - просто «свернуть»; массив массивов, уступая и переходя к следующему массиву итераций всех возможных комбинаций предыдущих символов. – ffriend 2 December 2010 в 04:14
  • 4
    Все еще не совсем работает, но я сближаюсь. – Yahel 2 December 2010 в 04:18
  • 5
    Я принимаю этот ответ и публикую свою «рабочую» версию. – Yahel 2 December 2010 в 04:21

Вам не нужна рекурсия или сильно вложенные петли или даже для создания / хранения всего массива перестановок в памяти.

Поскольку число перестановок является произведением длин каждой из массивы (назовем это numPerms), вы можете создать функцию getPermutation(n), которая возвращает уникальную перестановку между индексами 0 и numPerms - 1, вычисляя индексы, необходимые для извлечения своих символов, на основе n.

Как это делается? Если вы думаете о создании перестановок на массивах, каждый из которых содержит: [0, 1, 2, ... 9], это очень просто ... 245-я перестановка (n = 245) - это «245», скорее интуитивно, или:

arrayHundreds[Math.floor(n / 100) % 10]
+ arrayTens[Math.floor(n / 10) % 10]
+ arrayOnes[Math.floor(n / 1) % 10]

Усложнение в вашей проблеме заключается в том, что размеры массива различаются. Мы можем обойти это, заменив n/100, n/10 и т. Д. Другими делителями. С этой целью мы можем легко вычислить массив делителей. В приведенном выше примере делитель 100 был равен arrayTens.length * arrayOnes.length. Поэтому мы можем вычислить делитель для данного массива как произведение длин оставшихся массивов. У самого последнего массива всегда есть делитель 1. Кроме того, вместо modding на 10 мы модифицируем длину текущего массива.

Пример кода ниже:

var allArrays = [first, second, third, ...];

// Pre-calculate divisors
var divisors = [];
for (var i = allArrays.length - 1; i >= 0; i--) {
   divisors[i] = divisors[i + 1] ? divisors[i + 1] * allArrays[i + 1].length : 1;
}

function getPermutation(n) {
   var result = "", curArray;

   for (var i = 0; i < allArrays.length; i++) {
      curArray = allArrays[i];
      result += curArray[Math.floor(n / divisors[i]) % curArray.length];
   }

   return result;
}
18
ответ дан David Tang 16 August 2018 в 12:02
поделиться
  • 1
    Очень хорошо. Здесь есть опечатка, results должна показывать result - я замечаю, что вы обратились назад для вычисления делителей, я полагаю, что положение делителя в массиве важно? – Gary Green 14 April 2011 в 12:05
  • 2
    @Gary, спасибо, что выбрали это. Порядок делителей имеет значение, потому что первый зависит от второго, второй зависит от третьего и т. Д. Итак, зацикливаясь назад, я могу легче это построить. – David Tang 14 April 2011 в 12:36
  • 3
    @ Box9: Работает ли эта функция с 1 массивом? Разве это не (n * n) - (n-1)? – Bytemain 14 April 2011 в 12:53
  • 4
    @epitaph, он все равно должен работать с 1 массивом. divisors будет иметь только один элемент: [1], и поэтому он всегда будет делиться на 1, а mod - на длину массива - фактически ничего не делая. – David Tang 14 April 2011 в 12:57
  • 5
    Если он работает на 1 массив, и результат (n * n) - (n-1), я могу использовать его для создания матрицы затрат? Например, для проблемы travelsalesman? – Bytemain 14 April 2011 в 13:10

Предоставленные ответы слишком сложны для меня. Поэтому мое решение:

var allArrays = new Array(['a', 'b'], ['c', 'z'], ['d', 'e', 'f']);

function getPermutation(array, prefix) {
    prefix = prefix || '';
    if (!array.length) {
        return prefix;
    }

    var result = array[0].reduce(function (result, value) {
        return result.concat(getPermutation(array.slice(1), prefix + value));
    }, []);
    return result;
}

console.log(getPermutation(allArrays));
13
ответ дан Jonatas Walker 16 August 2018 в 12:02
поделиться
  • 1
    Шутки в сторону? это так просто. -_- Спасибо чувак. – ssi-anik 13 May 2016 в 07:09
  • 2
    Здравствуй. Как это можно изменить, чтобы возвращать массив массивов вместо массива строк? Таким образом, вместо "acd", "ace", "acf" ...] для возврата [["a", "c", d "], [" a "," c "," e "] ....] – Thomas 14 August 2017 в 14:17
  • 3
    @Thomas проверить эту скрипку jsfiddle.net/ponmudi/hoLpt4hn – Ponmudi VN 16 August 2017 в 05:33

Вот версия, адаптированная из приведенной выше пары ответов, которая производит результаты в порядке, указанном в OP, и возвращает строки вместо массивов:

function *cartesianProduct(...arrays) {
  if (!arrays.length) yield [];
  else {
    const [tail, ...head] = arrays.reverse();
    const beginning = cartesianProduct(...head.reverse());
    for (let b of beginning) for (let t of tail) yield b + t;
  }
}

const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third =  ['f', 'g', 'h', 'i', 'j'];
console.log([...cartesianProduct(first, second, third)])
0
ответ дан Klortho 16 August 2018 в 12:02
поделиться

Я предлагаю простую рекурсивную функцию -генератора следующим образом:

// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
  let remainder = tail.length ? cartesian(...tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}

// Example:
const first  = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third  = ['f', 'g', 'h', 'i', 'j'];

console.log(...cartesian(first, second, third));

9
ответ дан le_m 16 August 2018 в 12:02
поделиться
  • 1
    Какой красивый кусок кода :) – pietrop 19 December 2017 в 12:29
  • 2
    Это действительно красиво, но есть вещь, речь идет не о комбинациях, а о перестановках. В комбинациях для [1,2,3] & amp; [1,2,3] не должно быть выхода [1,2] & amp; [2,1], поскольку они являются одинаковыми в терминах комбинации. – ElSajko 10 August 2018 в 11:15
  • 3
    @ElSajko Хорошее наблюдение, термин «комбинации», выбранный OP, не является точным. Однако «перестановки» также не соответствуют сгенерированным значениям. ИМХО «декартовый продукт» - это хорошо, но я открыт для лучших предложений. – le_m 10 August 2018 в 12:40

Если вы ищете функцию, совместимую с потоком, которая может обрабатывать двухмерные массивы с любым типом элемента, вы можете использовать функцию ниже.

const getUniqueCombinations = <T>(items : Array<Array<T>>, prepend : Array<T> = []) : Array<Array<T>> => {
    if(!items || items.length === 0) return [prepend];

    let out = [];

    for(let i = 0; i < items[0].length; i++){
        out = [...out, ...getUniqueCombinations(items.slice(1), [...prepend, items[0][i]])];
    }

    return out;
}

Визуализация операции:

in:

[
    [Obj1, Obj2, Obj3],
    [Obj4, Obj5],
    [Obj6, Obj7]
]

out:

[
    [Obj1, Obj4, Obj6 ],
    [Obj1, Obj4, Obj7 ],
    [Obj1, Obj5, Obj6 ],
    [Obj1, Obj5, Obj7 ],
    [Obj2, Obj4, Obj6 ],
    [Obj2, Obj4, Obj7 ],
    [Obj2, Obj5, Obj6 ],
    [Obj2, Obj5, Obj7 ],
    [Obj3, Obj4, Obj6 ],
    [Obj3, Obj4, Obj7 ],
    [Obj3, Obj5, Obj6 ],
    [Obj3, Obj5, Obj7 ]
]
1
ответ дан Marthijn Bontekoning 16 August 2018 в 12:02
поделиться

Вы можете использовать типичный обратный трафик:

function cartesianProductConcatenate(arr) {
  var data = new Array(arr.length);
  return (function* recursive(pos) {
    if(pos === arr.length) yield data.join('');
    else for(var i=0; i<arr[pos].length; ++i) {
      data[pos] = arr[pos][i];
      yield* recursive(pos+1);
    }
  })(0);
}

Я использовал функции генератора, чтобы избежать одновременного выделения всех результатов, но если вы хотите, вы можете

[...cartesianProductConcatenate([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']])];
// ["acd","ace","acf","azd","aze","azf","bcd","bce","bcf","bzd","bze","bzf"]
5
ответ дан Oriol 16 August 2018 в 12:02
поделиться

Вы также можете использовать эту функцию:

const result = (arrayOfArrays) => arrayOfArrays.reduce((t, i) => { let ac = []; for (const ti of t) { for (const ii of i) { ac.push(ti + '/' + ii) } } return ac })

result([['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']])
// which will output [ 'a/e/f', 'a/e/g', 'a/e/h','a/e/i','a/e/j','b/e/f','b/e/g','b/e/h','b/e/i','b/e/j','c/e/f','c/e/g','c/e/h','c/e/i','c/e/j','d/e/f','d/e/g','d/e/h','d/e/i','d/e/j']

Конечно, вы можете удалить + '/' в ac.push(ti + '/' + ii), чтобы исключить косую черту из конечного результата. И вы можете заменить те for (... of ...) на функции forEach (плюс соответствующую точку с запятой до return ac), независимо от того, с кем вам удобнее.

0
ответ дан Renato Echevarria 16 August 2018 в 12:02
поделиться

Копия ответа le_m для непосредственного захвата массивов массивов:

function *combinations(arrOfArr) {
  let [head, ...tail] = arrOfArr
  let remainder = tail.length ? combinations(tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}

Надеюсь, это сэкономит чье-то время.

4
ответ дан Vikas Gautam 16 August 2018 в 12:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: