Получение всех возможных перестановок строки или комбинации, включая повторяющиеся символы в Java

Я пытался создать список всех возможных 4 символьных строк, которые могут быть составлены из любого заданного набора символов. Я использовал функцию для генерации каждой 4-символьной комбинации из набора символов, но каждый символ используется только один раз. Мне нужна каждая возможная комбинация, используя данный набор символов, например:

String[] elements = {"a", "b", "c", "1", "2", "3"};
int[] indices;
CombinationGenerator x = new CombinationGenerator (elements.length, 4);
StringBuffer combination;
while (x.hasMore ()) {
  combination = new StringBuffer ();
  indices = x.getNext ();
  for (int i = 0; i < indices.length; i++) {
      combination.append (elements[indices[i]]);
  }
  System.out.println (combination.toString ());
}

использование класса CombinationGenerator из здесь, это вернет каждую уникальную комбинацию символов 4, такую как as:

'abcd' , 'abc1', 'acb2', 'acb1'

но я хочу каждую возможную строку, которая может быть создана с использованием заданных символов. Например:

'aaaa', 'aaab', 'abc1', 'aac1', '11c2'

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

любой помощь, или даже просто теория о том, как это можно сделать, была бы полезной.

5 ответов


вы должны быть более конкретными в том, что именно вы хотите, чтобы ваша функция получила. Существует много различных определений "комбинаций", и вы не указали, хотите ли вы упорядоченные или неупорядоченные комбинации.

математически, если у вас есть n элементов и вам нужен список из k из них (упорядоченный с повторами), это дает вам

n ^ k

комбинаций. (6 ^ 4 = 1296 комбинаций в исходном примере, что очень много!). Однако, если у вас н элементы и хотят МУЛЬТИСЕТЬ из k из них (неупорядоченных с повторами), что дает вам

(n + k - 1)! / (k! * (n - 1)!)

комбинации и является гораздо более сложным перечислением.

если k мало, вы можете создать первый с ограниченным количеством циклов for, но это становится громоздким очень быстро, как K растет. Это сильно намекает на необходимость рекурсивного метода:

public static String[] getAllLists(String[] elements, int lengthOfList)
{
    //initialize our returned list with the number of elements calculated above
    String[] allLists = new String[(int)Math.pow(elements.length, lengthOfList)];

    //lists of length 1 are just the original elements
    if(lengthOfList == 1) return elements; 
    else
    {
        //the recursion--get all lists of length 3, length 2, all the way up to 1
        String[] allSublists = getAllLists(elements, lengthOfList - 1);

        //append the sublists to each element
        int arrayIndex = 0;

        for(int i = 0; i < elements.length; i++)
        {
            for(int j = 0; j < allSublists.length; j++)
            {
                //add the newly appended combination to the list
                allLists[arrayIndex] = elements[i] + allSublists[j];
                arrayIndex++;
            }
        }

        return allLists;
    }
}

этот метод не только создаст все списки, но и перечислит их по порядку. То есть выход будет

aaaa
aaab
aaac
aaa1
aaa2
aaa3
aaba
aabb
aabc
aab1
...
3323
333a
333b
333c
3331
3332
3333

использование исходного ввода. Он также может генерировать любую длину слов (будьте очень осторожны с этим! Просто со словами длины 8 я закончил с 1,679,616 комбинациями!).

если метод смущает вас (это рекурсивный метод, поэтому его немного трудно следовать) или если вы хотите решить вторую комбинационную проблему, не стесняйтесь спрашивать. Кроме того, этот метод несколько неэффективен, поскольку он пересчитывает комбинации для всех подсписков, поэтому он не подходит для очень длинных списков. Если вы действительно хотите эффективности, вы сохраните уже вычисленные кортежи в глобальном списке.


Если вы хотите его в Python, вам не нужно уметь программировать!

import itertools
for p in itertools.permutations('abc123', 4):
    print ''.join(p)

рекурсивные решения тоже кажутся довольно простыми:

public class TestCombinations {

public static void main(String[] args) {
    String[] elements = {"a", "b", "c", "1", "2", "3"};
    int maxLength = 4;
    combineStringFromElements(elements, "", maxLength);
}

private static void combineStringFromElements(String[] elements, String currentString, int maxLength) {
    if (currentString.length() == maxLength) {
        System.out.println(currentString);
        return;
    }
    for (String element : elements) {
        combineStringFromElements(elements, currentString + element, maxLength);
    }
}

}

код не является ни чистым, ни эффективным, просто чтобы продемонстрировать логику.


вы можете рассматривать ваши элементы как цифры. Подумайте о том, как мы получаем каждую возможную комбинацию "0" - "9" путем подсчета. Начать с 0000, 0001, 0002, ..., 0010, 0011, etc. Используйте тот же процесс, как если бы у вас была система чисел base-6 (или base-n, где n-длина вашего elements массив.

aaaa, aaab, aaac, ...,
aaba, aabb, aabc, aab1, aab2, aab3, .....,
aa32, aa33, abaa, etc.

чередуйте каждую комбинацию в последней цифре, затем продвигайте предыдущую цифру и повторяйте. Когда предпоследняя цифра прошла через каждый элемент, тогда продвиньте цифру с третьего по последний и так далее. Когда вы достигнете "3333", вы закончите.

ваш код будет примерно таким:

string comb;
for (int i = 0; i < elements.length; i++)
    for (int j = 0; j < elements.length; j++)
        for (int k = 0; k < elements.length; k++)
            for (int m = 0; m < elements.length; m++) {
                comb = elements[i] + elements[j] + elements[k] + elements[m];
                // do something with the combination
            }

есть и другие способы выполнить то же самое, что более эффективно, например, хранение промежуточных 1, 2, 3-символьных строк. Существуют также рекурсивные решения. Но это общая идея, и она должна быть достаточно быстрой для размера данных, который вы используете сейчас (у вас есть в общей сложности 6^4 = 1296 комбинаций.


вот некоторый код python, который делает то, что вы хотите:

answer = []
def permute(chars, s=''):
    if len(s) == 4:
        answer.append(s)
    else:
        for char in chars:
            permute(chars, s+char)

надеюсь, что это помогает

этот тип алгоритма называется рекурсивного спуска, если вы еще не проверили это.