Как бы вы протестировали все возможные комбинации дополнений из заданного набора N
чисел, чтобы они суммировались с заданным окончательным числом?
Краткий пример:
- Набор номеров для добавления:
N = {1,5,22,15,0,...}
- Желаемый результат:
12345
Как бы вы протестировали все возможные комбинации дополнений из заданного набора N
чисел, чтобы они суммировались с заданным окончательным числом?
Краткий пример:
N = {1,5,22,15,0,...}
12345
Ответы:
Эта проблема может быть решена с помощью рекурсивных комбинаций всех возможных сумм, отфильтровывающих те, которые достигают цели. Вот алгоритм в Python:
Этот тип алгоритмов очень хорошо объяснен в следующей лекции Standford по абстрактному программированию - это видео очень рекомендуется, чтобы понять, как работает рекурсия для генерации перестановок решений.
редактировать
Выше, как функция генератора, что делает его немного более полезным. Требуется Python 3.3+ из-за
yield from
.Вот Java-версия того же алгоритма:
Это точно такая же эвристика. Моя Java немного ржавая, но я думаю, что это легко понять.
C # преобразование решения Java: (@JeremyThompson)
Решение Ruby: (@emaillenin)
Редактировать: обсуждение сложности
Как уже упоминали другие, это NP-сложная проблема . Его можно решить за экспоненциальное время O (2 ^ n), например, при n = 10 будет 1024 возможных решения. Если цели, которые вы пытаетесь достичь, находятся в низком диапазоне, то этот алгоритм работает. Так, например:
subset_sum([1,2,3,4,5,6,7,8,9,10],100000)
генерирует 1024 ветви, потому что цель никогда не получает возможность отфильтровать возможные решения.С другой стороны,
subset_sum([1,2,3,4,5,6,7,8,9,10],10)
генерирует только 175 ветвей, потому что цель, которую нужно достичь,10
отфильтровывает множество комбинаций.Если
N
иTarget
большие числа, следует перейти к приблизительному варианту решения.источник
[1, 2, 0, 6, -3, 3], 3
- он выводит только[1,2], [0,3], [3]
при пропущенных случаях, таких как[6, -3, 3]
[1, 2, 5], 5
только выходы[5]
, когда[1, 1, 1, 1, 1]
,[2, 2, 1]
и[2, 1, 1, 1]
являются решениями.i+1
вremaining = numbers[i+1:]
. Похоже, что алгоритм не допускает дублирования.[1, 1, 3]
, посмотрите на stackoverflow.com/a/34971783/3684296 (Python)Решение этой проблемы было дано миллион раз в Интернете. Проблема называется проблемой смены монет . Решения можно найти по адресу http://rosettacode.org/wiki/Count_the_coins и его математическую модель по адресу http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (или обмен монет Google проблема ).
Кстати, решение Scala от Tsagadai, интересно. В этом примере выводится либо 1, либо 0. В качестве побочного эффекта на консоли выводятся все возможные решения. Он отображает решение, но не делает его пригодным для использования каким-либо образом.
Чтобы быть как можно более полезным, код должен возвращать a
List[List[Int]]
, чтобы можно было получить номер решения (длина списка списков), «лучшее» решение (самый короткий список) или все возможные решения.Вот пример. Это очень неэффективно, но это легко понять.
При запуске он отображает:
sumCombinations()
Функция может быть использована сама по себе, и результат может быть дополнительно проанализированы , чтобы отобразить «лучший» решение (самый короткий список), или число решений (количество списков).Обратите внимание, что даже в этом случае требования могут быть не полностью выполнены. Может случиться так, что порядок каждого списка в решении будет значительным. В таком случае каждый список должен дублироваться столько раз, сколько существует комбинация его элементов. Или нас могут интересовать только разные комбинации.
Например, мы могли бы рассмотреть, что
List(5, 10)
должно дать две комбинации:List(5, 10)
иList(10, 5)
. ЗаList(5, 5, 5)
это можно дать три комбинации или только одну, в зависимости от требований. Для целых чисел три перестановки эквивалентны, но если мы имеем дело с монетами, как в «проблеме смены монет», то это не так.В требованиях также не указан вопрос о том, можно ли использовать каждый номер (или монету) только один или несколько раз. Мы могли бы (и мы должны!) Обобщить проблему в список списков вхождений каждого числа. В реальной жизни это переводится как «как можно заработать определенную сумму денег с помощью набора монет (а не набора ценностей монет)». Первоначальная проблема - это только частный случай этой проблемы, когда у нас есть столько экземпляров каждой монеты, сколько необходимо, чтобы составить общую сумму для каждой отдельной стоимости монеты.
источник
В Хаскеле :
И J :
Как вы можете заметить, оба используют один и тот же подход и делят проблему на две части: генерируют каждого члена набора мощности и проверяют сумму каждого члена для цели.
Есть и другие решения, но это самое простое.
Вам нужна помощь или с одним, или найти другой подход?
источник
not in scope: 'subsequences'
есть указатели?import Data.List
Версия Javascript:
источник
remaining = numbers.slice();
remaining.slice(i + 1);
противном случае следуетnumbers.slice(i + 1);
изменить массив чиселslice
возвращает (мелкую) копию, но не изменяетnumbers
массив.C ++ версия того же алгоритма
источник
C # версия ответа на код @msalvadores
источник
Я думал, что буду использовать ответ на этот вопрос, но не смог, так что вот мой ответ. Он использует модифицированную версию ответа в разделе «Структура и интерпретация компьютерных программ» . Я думаю, что это лучшее рекурсивное решение, и оно должно больше радовать пуристов.
Мой ответ в Scala (и извиняюсь, если мой Scala отстой, я только начал изучать его). FindSumCombinations сумасшествие является своего рода уникальным и первоначальный список для рекурсии , чтобы предотвратить простофили.
Чтобы использовать это:
источник
я преобразовал выше логику из питона в php ..
источник
Другое решение на Python - использовать
itertools.combinations
модуль следующим образом:Вывод:
[(8, 7), (5, 10), (3, 8, 4), (3, 5, 7)]
источник
Вот решение в R
источник
subset_sum(numbers = c(1:2), target = 5)
возвращает"sum(1+2+2)=5"
. Но комбинация 1 + 1 + 1 + 1 + 1 отсутствует. Установка целей на более высокие числа (например, 20) пропускает еще больше комбинаций.subset_sum(1:2, 4)
не должен возвращать никаких решений, потому что нет комбинации 1 и 2, которая добавляет к 4. То, что нужно добавить в мою функцию, это escape, еслиi
оно больше, чем длинаnumbers
Вот Java-версия, которая хорошо подходит для малого N и очень большой целевой суммы, когда сложность
O(t*N)
(динамическое решение) больше, чем экспоненциальный алгоритм. Моя версия использует встречу в середине атаки, а также немного смещается, чтобы уменьшить сложность от классического наивногоO(n*2^n)
доO(2^(n/2))
.Если вы хотите использовать это для наборов с количеством элементов от 32 до 64, вы должны изменить значение,
int
представляющее текущее подмножество в функции шага, на значение,long
хотя производительность, очевидно, резко уменьшится с увеличением размера набора. Если вы хотите использовать это для набора с нечетным числом элементов, вы должны добавить 0 к набору, чтобы сделать его четным.источник
Это похоже на проблему замены монет
источник
Очень эффективный алгоритм с использованием таблиц, которые я написал на С ++ пару лет назад.
Если вы установите PRINT 1, он напечатает все комбинации (но не будет использовать эффективный метод).
Он настолько эффективен, что рассчитывает более 10 ^ 14 комбинаций менее чем за 10 мс.
источник
Версия Excel VBA ниже. Мне нужно было реализовать это в VBA (не мое предпочтение, не судите меня!), И использовал ответы на этой странице для подхода. Я загружаю в случае, если другие также нуждаются в версии VBA.
Вывод (записанный в окно Immediate) должен быть:
источник
Вот лучшая версия с лучшим форматированием вывода и возможностями C ++ 11:
источник
Нерекурсивная версия Java, которая просто продолжает добавлять элементы и перераспределять их среди возможных значений.
0
Они игнорируются и работают для фиксированных списков (то, что вам дано, с которыми вы можете играть) или списка повторяющихся чисел.Пример ввода:
Образец вывода:
источник
Чтобы найти комбинации с помощью Excel - (это довольно легко). (Ваш компьютер не должен быть слишком медленным)
Загрузите файл Excel "Sum to Target".
Следуйте инструкциям на странице сайта.
надеюсь это поможет.
источник
Swift 3 преобразование Java-решения: (@JeremyThompson)
использование:
источник
Это может быть использовано для печати всех ответов, а также
Сложность времени экспоненциальная. Порядка 2 ^ n
источник
Я делал что-то подобное для скала. Мысль о размещении моего решения здесь:
источник
Я портировал образец C # на Objective-c и не видел его в ответах:
источник
Ответ @ KeithBeller с немного измененными именами переменных и некоторыми комментариями.
источник
Версия PHP , вдохновленная версией Кейта Беллера на C #.
PHP-версия bala у меня не сработала, потому что мне не нужно было группировать числа. Я хотел более простую реализацию с одним целевым значением и пулом чисел. Эта функция также удалит любые повторяющиеся записи.
источник
Рекомендуется в качестве ответа:
Вот решение с использованием генераторов es2015 :
Использование генераторов на самом деле может быть очень полезным, поскольку оно позволяет приостановить выполнение скрипта сразу после нахождения допустимого подмножества. Это в отличие от решений без генераторов (т. Е. Без состояния), которые должны перебирать все подмножества
numbers
источник
Выведите 0 на первое место. Ноль является идентификатором сложения, поэтому он бесполезен по законам моноидов в данном конкретном случае. Также выведите отрицательные числа, если вы хотите подняться до положительного числа. В противном случае вам также потребуется операция вычитания.
Итак ... самый быстрый алгоритм, который вы можете получить на этой конкретной работе, приведен в JS.
Это очень быстрый алгоритм, но если вы сортируете
data
массив по убыванию, он будет еще быстрее. Использование.sort()
незначительно, так как алгоритм будет иметь гораздо меньше рекурсивных вызовов.источник
Версия Perl (ведущего ответа):
Результат:
Версия Javascript:
Javascript однострочный, который на самом деле возвращает результаты (вместо того, чтобы печатать его):
И мой любимый, однострочный с обратным вызовом:
источник
Это динамическое решение для JS, позволяющее определить, каким образом каждый может получить определенную сумму. Это может быть правильным решением, если вы думаете о сложности времени и пространства.
источник
источник
Мне не понравилось решение Javascript, которое я увидел по той причине, что я строю его в myselft, используя частичное применение, замыкания и рекурсию:
Хорошо, меня больше всего беспокоит вопрос о том, сможет ли массив комбинаций соответствовать целевому требованию, но с этим подходом вы можете начать находить остальные комбинации
Здесь просто установите цель и передайте массив комбинаций.
текущая реализация, которую я придумал
источник