генерация вариаций без повторов / перестановок в Java - PullRequest
7 голосов
/ 14 декабря 2009

Я должен сгенерировать все варианты без повторений, состоящих из цифр 0 - 9.

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

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

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

Ответы [ 9 ]

6 голосов
/ 14 декабря 2009

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

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

(То же, что сказал Даффимо: я не буду предоставлять код для этого)

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

2 голосов
/ 26 мая 2016

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

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;
    }
}
2 голосов
/ 18 июня 2015

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

  1. Сортировка снова массив символов. например: a1 a2 a3 b1 b2 b3 .... (a1 = a2 = a3)
  2. генерировать перестановку и всегда поддерживать условие: индекс a1 <индекс a2 <индекс a3 ... </li>
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 голосов
/ 15 августа 2016

Краткая справка по перестановке знаний

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

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

0 голосов
/ 17 октября 2015

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

    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 голосов
/ 15 сентября 2015

Перестановка без повторения основана на теореме, что количество результатов является факториалом количества элементов (в данном случае чисел). В вашем случае 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 голосов
/ 03 марта 2012

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

В приведенном выше коде отредактируйте цикл 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 голосов
/ 01 февраля 2010

используя Доллар это просто:

@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 голосов
/ 14 декабря 2009

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

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

например,

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

...

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

...