Выведите каждую комбинацию массива чисел с помощью javascript

У меня есть несколько чисел в массиве

var numArr = [1, 3, 5, 9];

Я хочу пройтись по этому массиву и умножить каждую уникальную комбинацию из трех чисел следующим образом:

1 * 3 * 5 = 
1 * 3 * 9 = 
1 * 5 * 9 = 
3 * 5 * 9 =

Затем верните массив всех вычислений

var ansArr = [15,27,45,135];

У кого-нибудь есть элегантное решение? Заранее спасибо.


person DaveKingsnorth    schedule 30.10.2010    source источник
comment
В заголовке вы просите перестановки, а в теле упоминаете комбинации. Что он? (Я предполагаю комбинации, так как умножение коммутативно.)   -  person Marcelo Cantos    schedule 31.10.2010
comment
@DaveKingsnorth Примечание. В вашем массиве есть строки, а не числа.   -  person Šime Vidas    schedule 31.10.2010
comment
@Sime Vidas - это была ошибка, это должны быть числа, а не строки   -  person DaveKingsnorth    schedule 31.10.2010
comment
@DaveKingsnorth Являются ли числа в массиве разными?   -  person Šime Vidas    schedule 31.10.2010
comment
@Sime Vidas - Не всегда в массиве могут быть повторяющиеся числа.   -  person DaveKingsnorth    schedule 31.10.2010
comment
@DaveKingsnorth Изменяется ли длина массива?   -  person Šime Vidas    schedule 31.10.2010
comment
@Sime Vidas - Да, массив может содержать от 2 до 10 чисел.   -  person DaveKingsnorth    schedule 31.10.2010
comment
Вы не в том же классе, что и stackoverflow.com/users/181557/ebae? stackoverflow.com/ вопросов/4057712/ (он получил лучшие ответы)   -  person xscott    schedule 31.10.2010


Ответы (5)


Общий алгоритм генерации комбинаций выглядит следующим образом:

function combinations(numArr, choose, callback) {
    var n = numArr.length;
    var c = [];
    var inner = function(start, choose_) {
        if (choose_ == 0) {
            callback(c);
        } else {
            for (var i = start; i <= n - choose_; ++i) {
                c.push(numArr[i]);
                inner(i + 1, choose_ - 1);
                c.pop();
            }
        }
    }
    inner(0, choose);
}

В вашем случае вы можете назвать это так:

function product(arr) {
    p = 1;
    for (var i in arr) {
        p *= arr[i];
    }
    return p;
}

var ansArr = [];

combinations(
    [1, 3, 5, 7, 9, 11], 3,
    function output(arr) {
        ansArr.push(product(arr));
    });

document.write(ansArr);

... что для данного ввода дает следующее:

15,21,27,33,35,45,55,63,77,99,105,135,165,189,231,297,315,385,495,693
person Marcelo Cantos    schedule 30.10.2010

Я думаю, что это должно работать:

var a = [1, 3, 5, 9];
var l = a.length;
var r = [];
for (var i = 0; i < l; ++i) {
  for (var j = i + 1; j < l; ++j) {
    for (var k = j + 1; k < l; ++k) {
      r.push(a[i] * a[j] * a[k]);
    }
  }
}

Изменить

Просто для собственного назидания я придумал универсальное решение, в котором вместо рекурсии используются циклы. Очевидным недостатком является то, что он длиннее и медленнее загружается или читается. С другой стороны (по крайней мере, в Firefox на моей машине) он работает примерно в два раза быстрее, чем рекурсивная версия. Однако я бы рекомендовал его только в том случае, если вы ищете комбинации для больших наборов или находите комбинации много раз на одной и той же странице. В любом случае, если кому-то интересно, вот что я придумал.

function combos(superset, size) {
  var result = [];
  if (superset.length < size) {return result;}
  var done = false;
  var current_combo, distance_back, new_last_index;
  var indexes = [];
  var indexes_last = size - 1;
  var superset_last = superset.length - 1;

  // initialize indexes to start with leftmost combo
  for (var i = 0; i < size; ++i) {
    indexes[i] = i;
  }

  while (!done) {
    current_combo = [];
    for (i = 0; i < size; ++i) {
      current_combo.push(superset[indexes[i]]);
    }
    result.push(current_combo);
    if (indexes[indexes_last] == superset_last) {
      done = true;
      for (i = indexes_last - 1; i > -1 ; --i) {
        distance_back = indexes_last - i;
        new_last_index = indexes[indexes_last - distance_back] + distance_back + 1;
        if (new_last_index <= superset_last) {
          indexes[indexes_last] = new_last_index;
          done = false;
          break;
        }
      }
      if (!done) {
        ++indexes[indexes_last - distance_back];
        --distance_back;
        for (; distance_back; --distance_back) {
          indexes[indexes_last - distance_back] = indexes[indexes_last - distance_back - 1] + 1;
        }
      }
    }
    else {++indexes[indexes_last]}
  }
  return result;
}

function products(sets) {
  var result = [];
  var len = sets.length;
  var product;
  for (var i = 0; i < len; ++i) {
    product = 1;
    inner_len = sets[i].length;
    for (var j = 0; j < inner_len; ++j) {
      product *= sets[i][j];
    }
    result.push(product);
  }
  return result;
}

console.log(products(combos([1, 3, 5, 7, 9, 11], 3)));
person Sid_M    schedule 30.10.2010
comment
Если бы я хотел получить все 2 комбинации чисел или 5 комбинаций чисел (если бы у меня был более длинный массив), есть ли решение, в котором я могу указать это значение как переменную (я говорю об указании длины комбинаций). - person DaveKingsnorth; 31.10.2010
comment
@DaveKingsnorth, если вы хотите изменить это, то мое решение слишком специфично. См. более общее решение Марсело. - person Sid_M; 31.10.2010
comment
Ваше решение на самом деле отвечает на мой первоначальный вопрос, но Марсело - это именно то, что мне нужно. Спасибо за ваш вклад. - person DaveKingsnorth; 31.10.2010

Рекурсивная функция для этого, когда вам нужно выбрать k чисел из n чисел. Не тестировал. Найдите ошибку и исправьте ее :-)

var result = [];

foo(arr, 0, 1, k, n); // initial call

function foo(arr, s, mul, k, n) {
    if (k == 1) {
        result.push(mul);
        return;
    }

    var i;
    for (i=s; i<=n-k; i++) {
        foo(arr, i+1, mul*arr[i], k-1, n-i-1);
    }
}

Это рекурсивная функция.

  1. Первый параметр - массив arr.

  2. Второй параметр — целое число s. Каждый вызов вычисляет значения для части массива, начиная с индекса s. Рекурсивно я увеличиваю s, поэтому массив для каждого вызова рекурсивно становится меньше.

  3. Третий параметр — это значение, которое вычисляется рекурсивно и передается в рекурсивном вызове. Когда k становится равным 1, он добавляется в массив результатов.

  4. k в желаемом размере комбинации. Он рекурсивно уменьшается, и когда становится равным 1, вывод добавляется в массив результатов.

  5. n - это размер массива arr. На самом деле n = arr.length

person Niraj Nawanit    schedule 31.10.2010
comment
@ Niraj - Можете ли вы объяснить, что такое каждый из параметров? - person DaveKingsnorth; 31.10.2010
comment
Это рекурсивная функция. 1) Первый параметр - массив обр. 2) Второй параметр — целое число s. Каждый вызов вычисляет значения для части массива, начиная с индекса s. Рекурсивно я увеличиваю s, поэтому массив для каждого вызова рекурсивно становится меньше. 3) Третий параметр — это значение, которое вычисляется рекурсивно и передается в рекурсивном вызове. Когда k становится равным 1, он добавляется в массив результатов. 4) k в размере желаемой комбинации. Он рекурсивно уменьшается, и когда становится равным 1, вывод добавляется в массив результатов. 5) n – размер массива обр. На самом деле n = длина обр. - person Niraj Nawanit; 31.10.2010
comment
Я так и думал, но все же где-то ошибся. Я вызываю функцию следующим образом: var arr = [2, 1, 4, 1, 6]; foo(обр, 0, 1, 4, 5); и вывести его следующим образом: document.write(result); Что я делаю неправильно? - person DaveKingsnorth; 02.11.2010

https://github.com/dankogai/js-combinatorics

Нашел эту библиотеку. Проверено на работоспособность. Ниже приведен документ из библиотеки:

var Combinatorics = require('js-combinatorics');
var cmb = Combinatorics.combination(['a','b','c','d'], 2);
while(a = cmb.next()) console.log(a);
//  ["a", "b"]
//  ["a", "c"]
//  ["a", "d"]
//  ["b", "c"]
//  ["b", "d"]
//  ["c", "d"]
person Bing Ren    schedule 13.11.2018

Используя узел, вы можете сделать это довольно легко, используя библиотеку. Сначала установите bit-twiddle с помощью npm:

npm install bit-twiddle

Затем вы можете использовать его в своем коде следующим образом:

//Assume n is the size of the set and k is the size of the combination
var nextCombination = require("bit-twiddle").nextCombination
for(var x=(1<<(k+1))-1; x<1<<n; x=nextCombination(x)) {
   console.log(x.toString(2))
}

Переменная x представляет собой битовый вектор, где бит i устанавливается, если iй элемент содержится в комбинации.

person Mikola    schedule 15.03.2013