Настоящим заявляется, что алгоритм в этой статье изменен с«Найти все возможные N чисел, сумма которых равна M, из массива — лучшее решение»одна статья. Отличие этой статьи в том, что используется двоичное представление положительной последовательности, которое более интуитивно понятно и проще в реализации.
вопрос
Найдите N чисел из массива, сумма которых равна всем возможным M.
Например, выберите 2 элемента из массива [1, 2, 3, 4] и просуммируйте все возможные 5. Ответ два набора комбинаций: 1,4 и 2,3.
Предположим, что функция-оболочкаsearch:
function search(arr, count, sum) {
...
return res
}
тогда есть,
search([1,2,3,4],2,5)
// => [[2,3],[1,4]]
Реализовать идеи
Здесь мы кратко говорим об общей идее: построить бинарные данные по длине массива, а затем выбрать данные, удовлетворяющие условиям.
Мы используем 1 и 0, чтобы указать, выбран ли элемент в массиве. Следовательно, 0110 можно использовать для указания того, что выбраны 1-й и 2-й биты в массиве.
Ниже приведен список всех представлений двоичных данных длины 4:
- 0000 означает отсутствие элементов в массиве
- 0001 означает, что выбран 3-й элемент в массиве
- 0010 означает, что выбран второй элемент в массиве
- 0011 означает, что выбраны 2-й и 3-й элементы в массиве
- 0100 означает, что выбран первый элемент в массиве
- 0101 означает, что выбраны 1-й и 3-й элементы в массиве
- 0110 означает, что выбраны 1-й и 2-й элементы в массиве
- 0111 означает, что выбраны 1-й, 2-й и 3-й элементы в массиве
- 1000 означает, что выбран 0-й элемент в массиве
- 1001 означает, что выбраны 0-й и 3-й элементы в массиве
- 1010 означает, что выбраны 0-й и 2-й элементы в массиве
- 1011 означает, что выбраны 0-й, 2-й и 3-й элементы в массиве
- 1110 означает, что выбраны 0-й, 1-й и 2-й элементы в массиве
- 1111 означает, что выбраны все битовые элементы в массиве
Итак, в примере в начале выберите 2 из 4, есть 6 вариантов двоичного кода, удовлетворяющего условиям: 0011, 0101, 0110, 1001, 1010 и 1100. Только 0110 и 1001 соответствуют сумме соответствующих элементов, равной 5.
Видите ли, идея в том, что мы строим все бины длины 4, а затем находим двоичные файлы, удовлетворяющие условиям.
Здесь есть два условия.
- Во-первых, выбранное число равно 2.
- Во-вторых, выбранная сумма равна 5.
Наши идеи алгоритма постепенно становятся ясными: Пройдитесь по всем двоичным файлам, определите, равно ли выбранное число 2, а затем найдите сумму соответствующих элементов, чтобы увидеть, равно ли оно 5.
Первый вопрос, как обойти все бинарные данные?
Нас сложно увидеть, длина массива 4, тогда все бинарные данные 0--15.
for (var i = 0; i < 16; i++) {
...
}
Длина массива равна 4, что соответствует 16, т.е. 1
Обратите внимание, что 1
Второй вопрос, как получить количество выбранных элементов? То есть как насчет нахождения количества единиц в двоичной строке?
Способов реализации много, например, один из них:
function n(i) {
var count = 0;
while( i ) {
if(i & 1){
++count;
}
i >>= 1;
}
return count;
}
console.log(n(0b1010))
// => 2
Идея приведенного выше алгоритма на самом деле очень проста, постепенно сдвигаем бинарник вправо на 1 бит, и видим количество единиц в конце. Например, двоичное значение 10 — это 1010, а все возможные сдвиги вправо — это 1010->101->10->1->0, 2 из которых равны 1 в конце. Итак, результат 2.
Третий вопрос, как суммировать на основе бинарных данных?
Например, 0110, мы должны суммировать arr[1] + arr[2].
Вопрос превращается в то, как судить, находится ли индекс массива в 0110?
На самом деле все очень просто, например, индекс 1 есть, а индекса 3 нет. Мы конвертируем 1 в 0100, 0110 и 0100 равны 0100, что больше 0, поэтому индекс 1 присутствует. А 0110 и 0001 равны 0, поэтому нижнего индекса 3 там нет.
Таким образом, суммирование мы можем сделать следующим образом:
var arr = [1,2,3,4]
var s = 0, temp = [];
for (var i = 0, len = arr.length; i < len; i++) {
if ( 0b0110 & 1 << (len - 1 - i)) {
s += arr[i]
temp.push(arr[i])
}
}
console.log(temp)
// => [2,3]
наконец понял
С приведенным выше предзнаменованием вот окончательная реализация:
function search(arr, count, sum) {
var len = arr.length, res = [];
for (var i = 0; i < Math.pow(2, len); i++) {
if (n(i) == count) {
var s = 0, temp = [];
for (var j = 0; j < len; j++) {
if (i & 1 << (len - 1 -j)) {
s += arr[j]
temp.push(arr[j])
}
}
if (s == sum) {
res.push(temp)
}
}
}
return res;
}
function n(i) {
var count = 0;
while( i ) {
if(i & 1){
++count;
}
i >>= 1;
}
return count;
}
console.log(search([1,2,3,4],2,5))
// => [[2,3],[1,4]]
Наконец, видно, что нет необходимости вводить различные понятия, чтобы сделать этот алгоритм понятным.
Эта статья закончилась.