генерация вариаций без повторов / перестановок в Java

7

Мне нужно сгенерировать все варианты без повторений из цифр 0-9.

Длина их может быть от 1 до 10. Я действительно не знаю, как ее решить, особенно, как избежать повторений.

Пример:  длина вариаций: 4  случайные вариации: 9856, 8753, 1243, 1234 и т.д. (но не 9985 - содержит повторение)

Я был бы очень благодарен, если кто-то может помочь мне в этом вопросе, особенно с указанием кода и подсказок.

Теги:
algorithm
permutation

9 ответов

6
Лучший ответ

Ключевое слово для поиска - это перестановка. Существует много свободного исходного кода, который их выполняет.

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

(То же, что и duffymo сказал: я не буду указывать код для этого)

Расширенное примечание: рекурсия основана на 0/1 (исключение, включение), которое может быть непосредственно переведено на биты, следовательно, целые числа. Поэтому, чтобы получить все возможные комбинации цифр, не выполняя собственно рекурсию, вы можете просто использовать все 10-битные целые числа и прокручивать их. Затем интерпретируйте числа таким образом, чтобы бит набора соответствовал включению цифры в список, который необходимо переместить.

  • 0
    Я не понимаю это решение. Похоже, вы генерируете все подмножества данного набора. Как вы переходите от подмножеств к перестановкам?
  • 0
    Как указано выше, это решение подчеркивает, как получить набор цифр и использует его в качестве входных данных для генерации всех соответствующих перестановок. Как на самом деле генерировать перестановки здесь выходит за рамки, но ресурсы на это доступны в изобилии.
1

Вот мой код Java. Не стесняйтесь спрашивать, не понимаете ли вы. Главное здесь:

  • сортировать массив символов. например: a1 a2 a3 b1 b2 b3.... (a1 = a2 = a3)
  • генерировать перестановку и всегда сохранять условие: индекс a1 < индекс a2 < индекс a3...
import java.util.Arrays;

public class PermutationDup {

    public void permutation(String s) {
        char[] original = s.toCharArray();
        Arrays.sort(original);
        char[] clone = new char[s.length()];
        boolean[] mark = new boolean[s.length()];
        Arrays.fill(mark, false);
        permute(original, clone, mark, 0, s.length());
    }

    private void permute(char[] original, char[] clone, boolean[] mark, int length, int n) {
        if (length == n) {
            System.out.println(clone);
            return;
        }

        for (int i = 0; i < n; i++) {
            if (mark[i] == true) continue;
            // dont use this state. to keep order of duplicate character
            if (i > 0 && original[i] == original[i-1] && mark[i-1] == false) continue;
            mark[i] = true;
            clone[length] = original[i];
            permute(original, clone, mark, length+1, n);
            mark[i] = false;
        }

    }

    public static void main(String[] args) {
        PermutationDup p = new PermutationDup();
        p.permutation("abcab");
    }
}
  • 0
    Я могу подтвердить, это работает. Проверено.
  • 0
    Что, если мы хотим получить перестановки с длиной <s.length?
0

Краткая полезная индексация перестановок Знание

Создайте метод, который генерирует правильную перестановку, учитывая значение индекса между {0 и N! -1} для "нулевой индексации" или {1 и N!} Для "одного индексированного".

Создайте второй метод, содержащий "цикл for", где нижняя граница равна 1, а верхняя граница - N!. например. "for (i; я <= N!; я ++)" для каждого экземпляра цикла вызывают первый метод, передавая я в качестве аргумента.

0

Я создал следующий код для генерации перестановок, где упорядочение важно и без повторения. Он использует generics для перестановки любого типа объекта:

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Permutations {

    public static <T> Collection<List<T>> generatePermutationsNoRepetition(Set<T> availableNumbers) {
        Collection<List<T>> permutations = new HashSet<>();

        for (T number : availableNumbers) {
            Set<T> numbers = new HashSet<>(availableNumbers);
            numbers.remove(number);

            if (!numbers.isEmpty()) {
                Collection<List<T>> childPermutations = generatePermutationsNoRepetition(numbers);
                for (List<T> childPermutation : childPermutations) {
                    List<T> permutation = new ArrayList<>();
                    permutation.add(number);
                    permutation.addAll(childPermutation);
                    permutations.add(permutation);
                }
            } else {
                List<T> permutation = new ArrayList<>();
                permutation.add(number);
                permutations.add(permutation);
            }
        }

        return permutations;
    }
}
0

Есть одно решение, которое не от меня, но оно очень красивое и сложное.

    package permutations;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * @author Vladimir Hajek
 *
 */
public class PermutationSimple {
    private static final int MAX_NUMBER = 3;

    Set<String> results = new HashSet<>(0);

    /**
     * 
     */
    public PermutationSimple() {
        // TODO Auto-generated constructor stub
    }

    /**
     * @param availableNumbers
     * @return
     */
    public static List<String> generatePermutations(Set<Integer> availableNumbers) {
        List<String> permutations = new LinkedList<>();

        for (Integer number : availableNumbers) {
            Set<Integer> numbers = new HashSet<>(availableNumbers);
            numbers.remove(number);

            if (!numbers.isEmpty()) {
                List<String> childPermutations = generatePermutations(numbers);
                for (String childPermutation : childPermutations) {
                    String permutation = number + childPermutation;
                    permutations.add(permutation);
                }
            } else {
                permutations.add(number.toString());
            }
        }

        return permutations;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        Set<Integer> availableNumbers = new HashSet<>(0);

        for (int i = 1; i <= MAX_NUMBER; i++) {
            availableNumbers.add(i);
        }

        List<String> permutations = generatePermutations(availableNumbers);
        for (String permutation : permutations) {
            System.out.println(permutation);
        }

    }
}

Я думаю, это отличное решение.

0

Пермутация без повторения основана на теореме, что количество результатов является факториальным из числа элементов (в этом случае чисел). В вашем случае 10! 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = 3628800. Доказательство того, что это точно, является правильным решением для поколения. Ну так как. На первой позиции, т.е. Слева, вы можете иметь 10 чисел, а на второй позиции у вас может быть только 9 чисел, потому что одно число находится в позиции слева, и мы не можем повторить то же число и т.д. (Доказательство проводится с помощью математической индукции). Итак, как генерировать первые десять результатов? Согласно моим знаниям, самым простым способом является использование циклического сдвига. Это означает, что порядок сдвига числа влево на одной позиции (или справа, если хотите), и число, которое переполняется, чтобы положить на пустое место. Это означает, что для первых десяти результатов:

10 9 8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1 10
8 7 6 5 4 3 2 1 10 9
7 6 5 4 3 2 1 10 9 8
6 5 4 3 2 1 10 9 8 7
5 4 3 2 1 10 9 8 7 6
...

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

На следующем шаге рекурсивно поворачивайте только 10-1 чисел в 10-1 раз и т.д. Это означает, что первые 9 результатов на втором шаге:

10 9 8 7 6 5 4 3 2 1
10 8 7 6 5 4 3 2 1 9
10 7 6 5 4 3 2 1 9 8
10 6 5 4 3 2 1 9 8 7
10 5 4 3 2 1 9 8 7 6
...

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

Алгоритм рекурсивно выполняет именно это, что объяснено выше. Можно создать все комбинации 3628800 для 10!, потому что число вложений такое же, как и количество элементов в массиве (это означает, что в вашем случае для 10 чисел он задерживается около 5 минут на моем компьютере), и вам нужно иметь достаточно памяти если вы хотите сохранить все комбинации в массиве.

Существует решение.

package permutation;

/** Class for generation amount of combinations (factorial)
 * !!! this is generate proper permutations without repeating and proper amount (počet) of rows !!!
 *
 * @author hariprasad
 */
public class TestForPermutationII {
  private static final String BUMPER = "*";
  private static int counter = 0;
  private static int sumsum = 0;
  // definitoin of array for generation
  //int[] testsimple = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  int[] testsimple = {1, 2, 3, 4, 5};
  private int ELEMNUM = testsimple.length;
  int[][] shuff;

  private String gaps(int len) {
    String addGap = "";
    for(int i=0; i <len; i++)
      addGap += "  ";
    return addGap;
  }

  /** Factorial computing */
  private int fact(int num) {
    if (num > 1) {
      return num * fact(num - 1);
    } else {
      return 1;
    }
  }

  /** Cyclic shift position to the left */  
  private int[] lShiftPos(int[] arr, int pos) {
    int[] work = new int[ELEMNUM];
    int offset = -1;
    for (int jj = 0; jj < arr.length; jj++) {
      if (jj < pos) {
        work[jj] = arr[jj];
      } else if (jj <= arr.length - 1) {
        if (jj == pos) {
          offset = arr[pos]; // last element
        }
        if (jj != (arr.length - 1)) {
          work[jj] = arr[jj + 1];
        } else {
          work[jj] = offset;
        }
      }
    }
    return work;
  }

  private String printBuff(int[] buffer) {
    String res = "";
    for (int i= 0; i < buffer.length; i++) {
      if (i == 0) 
        res += buffer[i];
      else
        res += ", " + buffer[i];
    }
    return res;
  };

  /** Recursive generator for arbitrary length of array */
  private String permutationGenerator(int pos, int level) {
    String ret = BUMPER;
    int templen = counter;
    int[] work = new int[ELEMNUM];
    int locsumread = 0;
    int locsumnew = 0;
    //System.out.println("\nCalled level: " + level);

    for (int i = 0; i <= templen; i++) {
      work = shuff[i];
      sumsum++;
      locsumread++;
      for (int ii = 0; ii < pos; ii++) {
        counter++;
        sumsum++;
        locsumnew++;
        work = lShiftPos(work, level); // deep copy
        shuff[counter] = work;
      }
    }

    System.out.println("locsumread, locsumnew: " + locsumread + ", " + locsumnew);
    // if level == ELEMNUM-2, it means no another shift
    if (level < ELEMNUM-2) {
      ret = permutationGenerator(pos-1, level+1);
      ret = "Level " + level + " end.";
      //System.out.println(ret);
    }
    return ret;
  }

  public static void main(String[] argv) {
    TestForPermutationII test = new TestForPermutationII();
    counter = 0;
    int len = test.testsimple.length;
    int[] work = new int[len];

    test.shuff = new int[test.fact(len)][];

    //initial
    test.shuff[counter] = test.testsimple;
    work = test.testsimple; // shalow copy

    test.shuff = new int[test.fact(len)][];
    counter = 0;
    test.shuff[counter] = test.testsimple;
    test.permutationGenerator(len-1, 0);

    for (int i = 0; i <= counter; i++) {
      System.out.println(test.printBuff(test.shuff[i]));
    }

    System.out.println("Counter, cycles: " + counter + ", " + sumsum);
  }
}

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

п! + n!/2! + n!/3! +... + n!/(n-2)! + n! (n-1)!

0

Код для этого похож на код без дубликатов, с добавлением оператора if-else. Отметьте это code

В приведенном выше коде измените цикл for следующим образом

for (j = i; j <= n; j++)
{

if(a[i]!=a[j] && !is_duplicate(a,i,j))              
    {
        swap((a+i), (a+j));
        permute(a, i+1, n);
        swap((a+i), (a+j)); 
    }
    else if(i!=j)  {}  // if no duplicate is present , do nothing           
    else permute(a,i+1,n);  // skip the ith character
}

bool is_duplicate(int *a,int i,int j) 
{
     if a[i] is present between a[j]...a[i] 
        return 1;
    otherwise
        return 0;

}

работал у меня

0

используя Dollar, это просто:

@Test
public void generatePermutations() {
    // digits is the string "0123456789"
    String digits = $('0', '9').join();

    // then generate 10 permutations
    for (int i : $(10)) {
        // shuffle, the cut (0, 4) in order to get a 4-char permutation
        System.out.println($(digits).shuffle().slice(4));
    }
}
  • 0
    Ссылка на доллар теперь не работает. Я надеюсь, что вы можете найти замену сломанной ссылке. : /
0

Представьте, что у вас есть магическая функция - заданный массив цифр, он вернет вам правильные перестановки.

Как вы можете использовать эту функцию для создания нового списка перестановок с помощью только одной дополнительной цифры?

например,

если я дал вам функцию под названием permute_three(char[3] digits), и я говорю вам, что она работает только для цифр 0, 1, 2, как вы можете написать функцию, которая может переставлять 0, 1, 2, 3, используя заданную функцию permute_three?

...

как только вы это решите, что вы заметили? можете ли вы его обобщить?

  • 0
    Для ОП: магия, связанная с этим, также известна как «рекурсия».
  • 0
    я надеялся не использовать слово r , так как это иногда пугает людей, начинающих ...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню