Я хочу написать функцию, которая принимает массив букв в качестве аргумента и количество этих букв для выбора.
Скажем, вы предоставляете массив из 8 букв и хотите выбрать 3 буквы из этого. Тогда вы должны получить:
8! / ((8 - 3)! * 3!) = 56
Взамен массивы (или слова), состоящие из 3 букв.
Ответы:
Искусство компьютерного программирования. Том 4: В Fascicle 3 есть тонна из них, которая может соответствовать вашей конкретной ситуации лучше, чем я описываю.
Серые коды
Проблема, с которой вы столкнетесь, это, конечно, память и довольно быстро, у вас будут проблемы с 20 элементами в вашем наборе - 20 C 3 = 1140. И если вы хотите перебрать набор, лучше всего использовать модифицированный серый закодируйте алгоритм, чтобы вы не держали их все в памяти. Они генерируют следующую комбинацию из предыдущего и избегают повторений. Есть много из них для различных целей. Мы хотим максимизировать различия между последовательными комбинациями? минимизировать? и так далее.
Некоторые из оригинальных работ, описывающих серые коды:
Вот некоторые другие документы, освещающие эту тему:
Чейз Твиддл (алгоритм)
Филип Дж. Чейз, « Алгоритм 382: комбинации М из N объектов » (1970)
Алгоритм в C ...
Индекс комбинаций в лексикографическом порядке (алгоритм Buckles 515)
Вы также можете ссылаться на комбинацию по ее индексу (в лексикографическом порядке). Понимая, что индекс должен быть некоторой величиной изменения справа налево на основе индекса, мы можем построить нечто, что должно восстановить комбинацию.
Итак, у нас есть набор {1,2,3,4,5,6} ... и мы хотим три элемента. Допустим, {1,2,3} мы можем сказать, что разница между элементами одна и в порядке и минимальна. {1,2,4} имеет одно изменение и лексикографически номер 2. Таким образом, число «изменений» в последнем месте соответствует одному изменению в лексикографическом порядке. Второе место с одним изменением {1,3,4} имеет одно изменение, но учитывает большее изменение, поскольку оно находится на втором месте (пропорционально количеству элементов в исходном наборе).
Метод, который я описал, является деконструкцией, как кажется, от установки к индексу, мы должны сделать обратное - что гораздо сложнее. Вот как Баксл решает проблему. Я написал несколько C для их вычисления с небольшими изменениями - я использовал индекс наборов, а не диапазон номеров, чтобы представить набор, поэтому мы всегда работаем от 0 ... n. Замечания:
Индекс комбинаций в лексикографическом порядке (McCaffrey)
Есть и другой способ : его концепцию легче понять и запрограммировать, но без оптимизации Buckles. К счастью, он также не создает дублирующихся комбинаций:
Набор, который максимизирует , где .
В качестве примера:
27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)
. Итак, 27-я лексикографическая комбинация из четырех вещей: {1,2,5,6}, это индексы любого набора, на который вы хотите посмотреть. Пример ниже (OCaml), требуетchoose
функции, оставленной читателю:Маленький и простой итератор комбинаций
Следующие два алгоритма предоставляются для дидактических целей. Они реализуют итератор и (более общие) общие комбинации папок. Они максимально быстрые, имеющие сложность O ( n C k ). Потребление памяти связано с
k
.Мы начнем с итератора, который будет вызывать пользовательскую функцию для каждой комбинации
Более общая версия будет вызывать предоставленную пользователем функцию вместе с переменной состояния, начиная с начального состояния. Поскольку нам нужно передать состояние между различными состояниями, мы не будем использовать цикл for, а вместо этого используем рекурсию,
источник
В C #:
Применение:
Результат:
источник
var result = new[] { 1, 2, 3, 4, 5 }.Combinations(3);
Краткое решение Java:
Результат будет
источник
Могу ли я представить свое рекурсивное решение Python для этой проблемы?
Пример использования:
Мне нравится это за его простоту.
источник
len(tuple(itertools.combinations('abcdefgh',3)))
достигнет того же самого в Python с меньшим количеством кода.for i in xrange(len(elements) - length + 1):
? Не имеет значения в Python, поскольку выход из индекса слайса обрабатывается изящно, но это правильный алгоритм.Допустим, ваш массив букв выглядит так: «ABCDEFGH». У вас есть три индекса (i, j, k), указывающие, какие буквы вы собираетесь использовать для текущего слова. Вы начинаете с:
Сначала вы меняете k, поэтому следующий шаг выглядит так:
Если вы достигли конца, вы продолжаете и меняете j, а затем снова k.
Как только вы достигли G, вы также начинаете меняться.
Написано в коде это выглядит примерно так
источник
Следующий рекурсивный алгоритм выбирает все комбинации k-элементов из упорядоченного набора:
i
вашей комбинацииi
с каждой из комбинацийk-1
элементов, выбранных рекурсивно из набора элементов больше, чемi
.Итерируйте выше для каждого
i
в наборе.Важно, чтобы вы выбрали остальные элементы как более крупные
i
, чтобы избежать повторения. Таким образом, [3,5] будет выбран только один раз, как [3] в сочетании с [5], а не дважды (условие исключает [5] + [3]). Без этого условия вы получаете вариации вместо комбинаций.источник
В C ++ следующая подпрограмма будет производить все комбинации длины (first, k) между диапазонами [first, last):
Это можно использовать так:
Это напечатает следующее:
источник
being
иbegin
сs.begin()
, иend
сs.end()
. Код близко следуетnext_permutation
алгоритму STL , описанному здесь более подробно.Я нашел эту ветку полезной и подумал, что добавлю решение Javascript, которое вы можете добавить в Firebug. В зависимости от вашего движка JS, если начальная строка велика, это может занять немного времени.
Вывод должен быть следующим:
источник
источник
Краткий пример на Python:
Для объяснения рекурсивный метод описан в следующем примере:
Пример: ABCDE
Все комбинации из 3 будут:
источник
Простой рекурсивный алгоритм в Haskell
Сначала мы определим частный случай, т.е. выберем нулевые элементы. Он выдает один результат, который представляет собой пустой список (т.е. список, который содержит пустой список).
Для n> 0,
x
проходит через каждый элемент списка иxs
после каждого элементаx
.rest
выбираетn - 1
элементы ,xs
используя рекурсивный вызовcombinations
. Конечным результатом функции является список, в котором каждый элементx : rest
(то есть список сx
заголовком иrest
хвостом) для каждого различного значенияx
иrest
.И, конечно, поскольку Haskell ленив, список постепенно генерируется по мере необходимости, поэтому вы можете частично оценить экспоненциально большие комбинации.
источник
А вот и дедушка КОБОЛЬ, очень злобный язык.
Давайте предположим, что массив состоит из 34 элементов по 8 байтов каждый (чисто произвольный выбор). Идея состоит в том, чтобы перечислить все возможные комбинации из 4 элементов и загрузить их в массив.
Мы используем 4 индекса, по одному на каждую позицию в группе из 4
Массив обрабатывается так:
Мы меняем idx4 от 4 до конца. Для каждого idx4 мы получаем уникальную комбинацию из четырех групп. Когда idx4 подходит к концу массива, мы увеличиваем idx3 на 1 и устанавливаем idx4 в idx3 + 1. Затем мы снова запускаем idx4 до конца. Мы продолжаем таким образом, увеличивая idx3, idx2 и idx1 соответственно, пока позиция idx1 не станет меньше 4 от конца массива. Это завершает алгоритм.
Первые итерации:
Пример COBOL:
источник
Вот элегантная, обобщенная реализация в Scala, как описано в 99 Проблемах Scala .
источник
Если вы можете использовать синтаксис SQL - скажем, если вы используете LINQ для доступа к полям структуры или массива или напрямую обращаетесь к базе данных, в которой есть таблица с именем «Алфавит», с одним полем «Буква», вы можете изменить следующее код:
Это вернет все комбинации из 3 букв, независимо от того, сколько букв у вас в таблице «Алфавит» (это может быть 3, 8, 10, 27 и т. Д.).
Если вам нужны все перестановки, а не комбинации (т. Е. Вы хотите, чтобы «ACB» и «ABC» считались разными, а не появлялись только один раз), просто удалите последнюю строку (одну «И»), и все готово.
Постредактирование: перечитав вопрос, я понимаю, что нужен общий алгоритм, а не только конкретный для случая выбора 3 пунктов. Ответ Адама Хьюза является полным, к сожалению, я не могу его проголосовать (пока). Этот ответ прост, но работает только тогда, когда вам нужно ровно 3 элемента.
источник
Еще одна версия C # с ленивым генерированием индексов комбинации. Эта версия поддерживает единый массив индексов для определения соответствия между списком всех значений и значениями для текущей комбинации, т.е. постоянно использует O (k) дополнительное пространство в течение всего времени выполнения. Код генерирует отдельные комбинации, включая первую, за O (k) время.
Тестовый код:
Вывод:
источник
c b a
чего нет.https://gist.github.com/3118596
Есть реализация для JavaScript. Он имеет функции для получения k-комбинаций и всех комбинаций массива любых объектов. Примеры:
источник
Здесь у вас есть ленивая версия этого алгоритма, написанная на C #:
И тестовая часть:
Надеюсь, это поможет вам!
источник
У меня был алгоритм перестановки, который я использовал для проекта Euler, в Python:
Если
у вас должна быть вся необходимая комбинация без повторения, она вам нужна?
Это генератор, поэтому вы используете его примерно так:
источник
источник
Clojure версия:
источник
Допустим, ваш массив букв выглядит так: «ABCDEFGH». У вас есть три индекса (i, j, k), указывающие, какие буквы вы собираетесь использовать для текущего слова. Вы начинаете с:
Сначала вы меняете k, поэтому следующий шаг выглядит так:
Если вы достигли конца, вы продолжаете и меняете j, а затем снова k.
Как только вы достигли G, вы также начинаете меняться.
Основано на https://stackoverflow.com/a/127898/2628125 , но более абстрактно, для указателей любого размера.
источник
Все сказано и сделано, вот код O'caml для этого. Алгоритм очевиден из кода ..
источник
Вот метод, который дает вам все комбинации указанного размера из строки произвольной длины. Похоже на решение Кинмарса, но работает для различного ввода и k.
Код может быть изменен, чтобы обернуться, то есть 'dab' от ввода 'abcd' wk = 3.
Выход для "abcde":
источник
короткий код на Python, приводящий к позициям индекса
источник
Для этого я создал решение в SQL Server 2005 и разместил его на своем веб-сайте: http://www.jessemclain.com/downloads/code/sql/fn_GetMChooseNCombos.sql.htm.
Вот пример, чтобы показать использование:
Результаты:
источник
Вот мое предложение в C ++
Я попытался наложить как можно меньше ограничений на тип итератора, поэтому это решение предполагает только прямой итератор и может быть const_iterator. Это должно работать с любым стандартным контейнером. В случаях, когда аргументы не имеют смысла, он выдает std :: invalid_argumnent
источник
Вот код, который я недавно написал на Java, который вычисляет и возвращает все комбинации элементов «num» из элементов «outOf».
источник
Краткое решение Javascript:
источник
Алгоритм:
В C #:
Почему это работает?
Существует биекция между подмножествами n-элементного набора и n-битных последовательностей.
Это означает, что мы можем выяснить, сколько существует подмножеств, подсчитав последовательности.
например, четыре элемента, представленные ниже, могут быть представлены {0,1} X {0, 1} X {0, 1} X {0, 1} (или 2 ^ 4) различными последовательностями.
Итак, все, что нам нужно сделать, это считать от 1 до 2 ^ n, чтобы найти все комбинации. (Мы игнорируем пустое множество.) Затем переведите цифры в их двоичное представление. Затем замените элементы вашего набора на «биты».
Если вы хотите получить только k элементов, выведите их только тогда, когда k бит включены.
(Если вы хотите, чтобы все подмножества вместо k подмножеств длины, удалите часть cnt / kElement.)
(В качестве доказательства см. Бесплатный учебный курс MIT по математике для информатики, Lehman и др., Раздел 11.2.2. Https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-042j-matmatics- для информатики-осень-2010 / чтения / )
источник
Я написал класс для работы с общими функциями для работы с биномиальным коэффициентом, к которому относится ваша проблема. Он выполняет следующие задачи:
Выводит все K-индексы в хорошем формате для любого N, выбирают K в файл. K-индексы могут быть заменены более описательными строками или буквами. Этот метод делает решение этого типа проблемы довольно тривиальным.
Преобразует K-индексы в правильный индекс записи в отсортированной таблице биномиальных коэффициентов. Этот метод намного быстрее, чем старые опубликованные методы, основанные на итерации. Это делается с помощью математического свойства, присущего треугольнику Паскаля. Моя газета говорит об этом. Я считаю, что я первый, кто открыл и опубликовал эту технику, но я могу ошибаться.
Преобразует индекс в отсортированной таблице биномиальных коэффициентов в соответствующие K-индексы.
Использует метод Марка Домина для вычисления биномиального коэффициента, который с гораздо меньшей вероятностью переполняется и работает с большими числами.
Класс написан на .NET C # и предоставляет способ управления объектами, связанными с проблемой (если есть), с помощью общего списка. Конструктор этого класса принимает значение bool, называемое InitTable, которое при значении true создает общий список для хранения управляемых объектов. Если это значение равно false, таблица не будет создана. Таблицу не нужно создавать для выполнения 4 вышеуказанных методов. Методы доступа предоставляются для доступа к таблице.
Существует связанный тестовый класс, который показывает, как использовать класс и его методы. Он был тщательно протестирован с 2 случаями, и никаких известных ошибок нет.
Чтобы прочитать об этом классе и загрузить код, см. Таблизацию биномиального коэффициента .
Не должно быть трудно преобразовать этот класс в C ++.
источник