Перестановка массива

59

Например, у меня есть этот массив:

int a[] = new int[]{3,4,6,2,1};

Мне нужен список всех перестановок, так что, если это так, {3,2,1,4,6}, другие не должны быть одинаковыми. Я знаю, что если длина массива равна n, то есть n! возможные комбинации. Как можно записать этот алгоритм?

Обновление: спасибо, но мне нужен алгоритм псевдокода, например:

for(int i=0;i<a.length;i++){
    // code here
}

Просто алгоритм. Да, функции API хороши, но мне это не очень помогает.

  • 8
    Нет 2 ^ n возможных комбинаций . Нет! перестановки . Плюс я не понимаю вопроса. Вы просто пытаетесь исключить одну перестановку, {3,2,1,4,6} ?
  • 0
    да извините! не все перестановки должны быть уникальными
Показать ещё 2 комментария
Теги:
algorithm

11 ответов

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

Если вы используете C++, вы можете использовать std::next_permutation из заголовочного файла <algorithm>:

int a[] = {3,4,6,2,1};
int size = sizeof(a)/sizeof(a[0]);
std::sort(a, a+size);
do {
  // print a elements
} while(std::next_permutation(a, a+size));
119

Вот как вы можете распечатать все перестановки в 10 строках кода:

public class Permute{
    static void permute(java.util.List<Integer> arr, int k){
        for(int i = k; i < arr.size(); i++){
            java.util.Collections.swap(arr, i, k);
            permute(arr, k+1);
            java.util.Collections.swap(arr, k, i);
        }
        if (k == arr.size() -1){
            System.out.println(java.util.Arrays.toString(arr.toArray()));
        }
    }
    public static void main(String[] args){
        Permute.permute(java.util.Arrays.asList(3,4,6,2,1), 0);
    }
}

Вы берете первый элемент массива (k = 0) и обмениваете его с любым элементом (i) массива. Затем вы рекурсивно применяете перестановку в массиве, начиная со второго элемента. Таким образом вы получаете все перестановки, начиная с i-го элемента. Сложная часть состоит в том, что после рекурсивного вызова вы должны поменять i-й элемент на первый элемент назад, иначе вы могли бы получить повторяющиеся значения в первом месте. Поменяв его назад, мы восстанавливаем порядок элементов (в основном вы возвращаете назад).

Итераторы и расширение для случая повторяющихся значений

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

Например, при вводе [3,3,4,4] все возможные перестановки (без повторений) составляют

[3, 3, 4, 4]
[3, 4, 3, 4]
[3, 4, 4, 3]
[4, 3, 3, 4]
[4, 3, 4, 3]
[4, 4, 3, 3]

(если вы просто примените функцию permute сверху, вы получите [3,3,4,4] четыре раза, и это не то, что вы, естественно, хотите увидеть в этом случае, а число таких перестановок 4!/(2! * 2!) = 6)

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

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

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

Вот ядро ​​алгоритма:

//ind is an array of integers
for(int tail = ind.length - 1;tail > 0;tail--){
    if (ind[tail - 1] < ind[tail]){//still increasing

        //find last element which does not exceed ind[tail-1]
        int s = ind.length - 1;
        while(ind[tail-1] >= ind[s])
            s--;

        swap(ind, tail-1, s);

        //reverse order of elements in the tail
        for(int i = tail, j = ind.length - 1; i < j; i++, j--){
            swap(ind, i, j);
        }
        break;
    }
}

Вот полный код итератора. Конструктор принимает массив объектов и отображает их в массив целых чисел, используя HashMap.

import java.lang.reflect.Array;
import java.util.*;
class Permutations<E> implements  Iterator<E[]>{

    private E[] arr;
    private int[] ind;
    private boolean has_next;

    public E[] output;//next() returns this array, make it public

    Permutations(E[] arr){
        this.arr = arr.clone();
        ind = new int[arr.length];
        //convert an array of any elements into array of integers - first occurrence is used to enumerate
        Map<E, Integer> hm = new HashMap<E, Integer>();
        for(int i = 0; i < arr.length; i++){
            Integer n = hm.get(arr[i]);
            if (n == null){
                hm.put(arr[i], i);
                n = i;
            }
            ind[i] = n.intValue();
        }
        Arrays.sort(ind);//start with ascending sequence of integers


        //output = new E[arr.length]; <-- cannot do in Java with generics, so use reflection
        output = (E[]) Array.newInstance(arr.getClass().getComponentType(), arr.length);
        has_next = true;
    }

    public boolean hasNext() {
        return has_next;
    }

    /**
     * Computes next permutations. Same array instance is returned every time!
     * @return
     */
    public E[] next() {
        if (!has_next)
            throw new NoSuchElementException();

        for(int i = 0; i < ind.length; i++){
            output[i] = arr[ind[i]];
        }


        //get next permutation
        has_next = false;
        for(int tail = ind.length - 1;tail > 0;tail--){
            if (ind[tail - 1] < ind[tail]){//still increasing

                //find last element which does not exceed ind[tail-1]
                int s = ind.length - 1;
                while(ind[tail-1] >= ind[s])
                    s--;

                swap(ind, tail-1, s);

                //reverse order of elements in the tail
                for(int i = tail, j = ind.length - 1; i < j; i++, j--){
                    swap(ind, i, j);
                }
                has_next = true;
                break;
            }

        }
        return output;
    }

    private void swap(int[] arr, int i, int j){
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    public void remove() {

    }
}

Использование/тест:

    TCMath.Permutations<Integer> perm = new TCMath.Permutations<Integer>(new Integer[]{3,3,4,4,4,5,5});
    int count = 0;
    while(perm.hasNext()){
        System.out.println(Arrays.toString(perm.next()));
        count++;
    }
    System.out.println("total: " + count);

Распечатывает все перестановки 7!/(2!*3!*2!)=210.

  • 1
    Отличный ответ. Не могли бы вы объяснить, почему это 4! / (2! 2!) = 6, а не 4! / (2!) = 12
  • 1
    Прежде всего, я знаю, что ответ - 6 (из моего [3,3,4,4] примера). Чтобы получить формулу, представьте [3,3,4,4] как два синих и два красных шара. Вопрос в том, сколько способов позиционировать шары (шары одного цвета одинаковы). Если вы как-то расположите свои шары, то смена синих шаров (2! Способа сделать это) или двух красных шаров (2! Способа сделать это) ничего не изменит. Теперь у нас есть 4! способы размещения 4 шаров, но перестановка синих шаров (2! пути) или красных шаров (2! пути) не меняет расположение шаров. Таким образом, вы получите 4! / (2! * 2!) В качестве окончательного ответа
Показать ещё 3 комментария
16

Вот реализация Пермутации в Java:

Пермутация - Java

Вы должны проверить его!

Изменить: код, вставленный ниже, для защиты от death-death:

// Permute.java -- A class generating all permutations

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.lang.reflect.Array;

public class Permute implements Iterator {

   private final int size;
   private final Object [] elements;  // copy of original 0 .. size-1
   private final Object ar;           // array for output,  0 .. size-1
   private final int [] permutation;  // perm of nums 1..size, perm[0]=0

   private boolean next = true;

   // int[], double[] array won't work :-(
   public Permute (Object [] e) {
      size = e.length;
      elements = new Object [size];    // not suitable for primitives
      System.arraycopy (e, 0, elements, 0, size);
      ar = Array.newInstance (e.getClass().getComponentType(), size);
      System.arraycopy (e, 0, ar, 0, size);
      permutation = new int [size+1];
      for (int i=0; i<size+1; i++) {
         permutation [i]=i;
      }
   }

   private void formNextPermutation () {
      for (int i=0; i<size; i++) {
         // i+1 because perm[0] always = 0
         // perm[]-1 because the numbers 1..size are being permuted
         Array.set (ar, i, elements[permutation[i+1]-1]);
      }
   }

   public boolean hasNext() {
      return next;
   }

   public void remove() throws UnsupportedOperationException {
      throw new UnsupportedOperationException();
   }

   private void swap (final int i, final int j) {
      final int x = permutation[i];
      permutation[i] = permutation [j];
      permutation[j] = x;
   }

   // does not throw NoSuchElement; it wraps around!
   public Object next() throws NoSuchElementException {

      formNextPermutation ();  // copy original elements

      int i = size-1;
      while (permutation[i]>permutation[i+1]) i--;

      if (i==0) {
         next = false;
         for (int j=0; j<size+1; j++) {
            permutation [j]=j;
         }
         return ar;
      }

      int j = size;

      while (permutation[i]>permutation[j]) j--;
      swap (i,j);
      int r = size;
      int s = i+1;
      while (r>s) { swap(r,s); r--; s++; }

      return ar;
   }

   public String toString () {
      final int n = Array.getLength(ar);
      final StringBuffer sb = new StringBuffer ("[");
      for (int j=0; j<n; j++) {
         sb.append (Array.get(ar,j).toString());
         if (j<n-1) sb.append (",");
      }
      sb.append("]");
      return new String (sb);
   }

   public static void main (String [] args) {
      for (Iterator i = new Permute(args); i.hasNext(); ) {
         final String [] a = (String []) i.next();
         System.out.println (i);
      }
   }
}
  • 4
    +1, пожалуйста, добавьте соответствующий код к вашему сообщению, в случае, если ссылка когда-либо пойдет
  • 2
    Какая лицензия применяется к этому коду?
Показать ещё 1 комментарий
4

Это 2-перестановка для списка, завернутого в итератор

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/* all permutations of two objects 
 * 
 * for ABC: AB AC BA BC CA CB
 * 
 * */
public class ListPermutation<T> implements Iterator {

    int index = 0;
    int current = 0;
    List<T> list;

    public ListPermutation(List<T> e) {
        list = e;
    }

    public boolean hasNext() {
        return !(index == list.size() - 1 && current == list.size() - 1);
    }

    public List<T> next() {
        if(current == index) {
            current++;
        }
        if (current == list.size()) {
            current = 0;
            index++;
        }
        List<T> output = new LinkedList<T>();
        output.add(list.get(index));
        output.add(list.get(current));
        current++;
        return output;
    }

    public void remove() {
    }

}
3

Пример с примитивным массивом:

public static void permute(int[] intArray, int start) {
    for(int i = start; i < intArray.length; i++){
        int temp = intArray[start];
        intArray[start] = intArray[i];
        intArray[i] = temp;
        permute(intArray, start + 1);
        intArray[i] = intArray[start];
        intArray[start] = temp;
    }
    if (start == intArray.length - 1) {
        System.out.println(java.util.Arrays.toString(intArray));
    }
}

public static void main(String[] args){
    int intArr[] = {1, 2, 3};
    permute(intArr, 0);
}
3

Существует суммарные перестановки n! для заданного размера массива n. Вот код, написанный на Java с использованием DFS.

public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> results = new ArrayList<List<Integer>>();
    if (nums == null || nums.length == 0) {
        return results;
    }
    List<Integer> result = new ArrayList<>();
    dfs(nums, results, result);
    return results;
}

public void dfs(int[] nums, List<List<Integer>> results, List<Integer> result) {
    if (nums.length == result.size()) {
        List<Integer> temp = new ArrayList<>(result);
        results.add(temp);
    }        
    for (int i=0; i<nums.length; i++) {
        if (!result.contains(nums[i])) {
            result.add(nums[i]);
            dfs(nums, results, result);
            result.remove(result.size() - 1);
        }
    }
}

Для входного массива [3,2,1,4,6] всего 5!= 120 возможных перестановок:

[[3,4,6,2,1],[3,4,6,1,2],[3,4,2,6,1],[3,4,2,1,6],[3,4,1,6,2],[3,4,1,2,6],[3,6,4,2,1],[3,6,4,1,2],[3,6,2,4,1],[3,6,2,1,4],[3,6,1,4,2],[3,6,1,2,4],[3,2,4,6,1],[3,2,4,1,6],[3,2,6,4,1],[3,2,6,1,4],[3,2,1,4,6],[3,2,1,6,4],[3,1,4,6,2],[3,1,4,2,6],[3,1,6,4,2],[3,1,6,2,4],[3,1,2,4,6],[3,1,2,6,4],[4,3,6,2,1],[4,3,6,1,2],[4,3,2,6,1],[4,3,2,1,6],[4,3,1,6,2],[4,3,1,2,6],[4,6,3,2,1],[4,6,3,1,2],[4,6,2,3,1],[4,6,2,1,3],[4,6,1,3,2],[4,6,1,2,3],[4,2,3,6,1],[4,2,3,1,6],[4,2,6,3,1],[4,2,6,1,3],[4,2,1,3,6],[4,2,1,6,3],[4,1,3,6,2],[4,1,3,2,6],[4,1,6,3,2],[4,1,6,2,3],[4,1,2,3,6],[4,1,2,6,3],[6,3,4,2,1],[6,3,4,1,2],[6,3,2,4,1],[6,3,2,1,4],[6,3,1,4,2],[6,3,1,2,4],[6,4,3,2,1],[6,4,3,1,2],[6,4,2,3,1],[6,4,2,1,3],[6,4,1,3,2],[6,4,1,2,3],[6,2,3,4,1],[6,2,3,1,4],[6,2,4,3,1],[6,2,4,1,3],[6,2,1,3,4],[6,2,1,4,3],[6,1,3,4,2],[6,1,3,2,4],[6,1,4,3,2],[6,1,4,2,3],[6,1,2,3,4],[6,1,2,4,3],[2,3,4,6,1],[2,3,4,1,6],[2,3,6,4,1],[2,3,6,1,4],[2,3,1,4,6],[2,3,1,6,4],[2,4,3,6,1],[2,4,3,1,6],[2,4,6,3,1],[2,4,6,1,3],[2,4,1,3,6],[2,4,1,6,3],[2,6,3,4,1],[2,6,3,1,4],[2,6,4,3,1],[2,6,4,1,3],[2,6,1,3,4],[2,6,1,4,3],[2,1,3,4,6],[2,1,3,6,4],[2,1,4,3,6],[2,1,4,6,3],[2,1,6,3,4],[2,1,6,4,3],[1,3,4,6,2],[1,3,4,2,6],[1,3,6,4,2],[1,3,6,2,4],[1,3,2,4,6],[1,3,2,6,4],[1,4,3,6,2],[1,4,3,2,6],[1,4,6,3,2],[1,4,6,2,3],[1,4,2,3,6],[1,4,2,6,3],[1,6,3,4,2],[1,6,3,2,4],[1,6,4,3,2],[1,6,4,2,3],[1,6,2,3,4],[1,6,2,4,3],[1,2,3,4,6],[1,2,3,6,4],[1,2,4,3,6],[1,2,4,6,3],[1,2,6,3,4],[1,2,6,4,3]]

Надеюсь, что это поможет.

0

Согласно вики https://en.wikipedia.org/wiki/Heap%27s_algorithm

Алгоритм кучи генерирует все возможные перестановки n объектов. Впервые он был предложен BR Heap в 1963 году. Алгоритм сводит к минимуму движение: он генерирует каждую перестановку из предыдущей путем замены одной пары элементов; остальные n − 2 элемента не нарушены. В обзоре алгоритмов генерации перестановок, проведенном в 1977 году, Роберт Седжвик пришел к выводу, что в то время это был самый эффективный алгоритм генерации перестановок с помощью компьютера.

Поэтому, если мы хотим сделать это рекурсивно, код Sudo приведен ниже.

procedure generate(n : integer, A : array of any):
    if n = 1 then
          output(A)
    else
        for i := 0; i < n - 1; i += 1 do
            generate(n - 1, A)
            if n is even then
                swap(A[i], A[n-1])
            else
                swap(A[0], A[n-1])
            end if
        end for
        generate(n - 1, A)
    end if

Java-код:

public static void printAllPermutations(
        int n, int[] elements, char delimiter) {
    if (n == 1) {
        printArray(elements, delimiter);
    } else {
        for (int i = 0; i < n - 1; i++) {
            printAllPermutations(n - 1, elements, delimiter);
            if (n % 2 == 0) {
                swap(elements, i, n - 1);
            } else {
                swap(elements, 0, n - 1);
            }
        }
        printAllPermutations(n - 1, elements, delimiter);
    }
}

private static void printArray(int[] input, char delimiter) {
    int i = 0;
    for (; i < input.length; i++) {
        System.out.print(input[i]);
    }
    System.out.print(delimiter);
}

private static void swap(int[] input, int a, int b) {
    int tmp = input[a];
    input[a] = input[b];
    input[b] = tmp;
}

public static void main(String[] args) {
    int[] input = new int[]{0,1,2,3};
    printAllPermutations(input.length, input, ',');
}
0

Реализация через рекурсию (динамическое программирование), на Java, с контрольным примером (TestNG).


Код

PrintPermutation.java

import java.util.Arrays;

/**
 * Print permutation of n elements.
 * 
 * @author eric
 * @date Oct 13, 2018 12:28:10 PM
 */
public class PrintPermutation {
    /**
     * Print permutation of array elements.
     * 
     * @param arr
     * @return count of permutation,
     */
    public static int permutation(int arr[]) {
        return permutation(arr, 0);
    }

    /**
     * Print permutation of part of array elements.
     * 
     * @param arr
     * @param n
     *            start index in array,
     * @return count of permutation,
     */
    private static int permutation(int arr[], int n) {
        int counter = 0;
        for (int i = n; i < arr.length; i++) {
            swapArrEle(arr, i, n);
            counter += permutation(arr, n + 1);
            swapArrEle(arr, n, i);
        }
        if (n == arr.length - 1) {
            counter++;
            System.out.println(Arrays.toString(arr));
        }

        return counter;
    }

    /**
     * swap 2 elements in array,
     * 
     * @param arr
     * @param i
     * @param k
     */
    private static void swapArrEle(int arr[], int i, int k) {
        int tmp = arr[i];
        arr[i] = arr[k];
        arr[k] = tmp;
    }
}

PrintPermutationTest.java (тестовый пример через TestNG)

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * PrintPermutation test.
 * 
 * @author eric
 * @date Oct 14, 2018 3:02:23 AM
 */
public class PrintPermutationTest {
    @Test
    public void test() {
        int arr[] = new int[] { 0, 1, 2, 3 };
        Assert.assertEquals(PrintPermutation.permutation(arr), 24);

        int arrSingle[] = new int[] { 0 };
        Assert.assertEquals(PrintPermutation.permutation(arrSingle), 1);

        int arrEmpty[] = new int[] {};
        Assert.assertEquals(PrintPermutation.permutation(arrEmpty), 0);
    }
}
0

Делай как это...

import java.util.ArrayList;
import java.util.Arrays;

public class rohit {

    public static void main(String[] args) {
        ArrayList<Integer> a=new ArrayList<Integer>();
        ArrayList<Integer> b=new ArrayList<Integer>();
        b.add(1);
        b.add(2);
        b.add(3);
        permu(a,b);
    }

    public static void permu(ArrayList<Integer> prefix,ArrayList<Integer> value) {
        if(value.size()==0) {
            System.out.println(prefix);
        } else {
            for(int i=0;i<value.size();i++) {
                ArrayList<Integer> a=new ArrayList<Integer>();
                a.addAll(prefix);
                a.add(value.get(i));

                ArrayList<Integer> b=new ArrayList<Integer>();

                b.addAll(value.subList(0, i));
                b.addAll(value.subList(i+1, value.size()));

                permu(a,b);
            }
        }
    }

}
0

Простая реализация Java, см. С++ std::next_permutation:

public static void main(String[] args){
    int[] list = {1,2,3,4,5};
    List<List<Integer>> output = new Main().permute(list);
    for(List result: output){
        System.out.println(result);
    }

}

public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> list = new ArrayList<List<Integer>>();
    int size = factorial(nums.length);

    // add the original one to the list
    List<Integer> seq = new ArrayList<Integer>();
    for(int a:nums){
        seq.add(a);
    }
    list.add(seq);

    // generate the next and next permutation and add them to list
    for(int i = 0;i < size - 1;i++){
        seq = new ArrayList<Integer>();
        nextPermutation(nums);
        for(int a:nums){
            seq.add(a);
        }
        list.add(seq);
    }
    return list;
}


int factorial(int n){
    return (n==1)?1:n*factorial(n-1);
}

void nextPermutation(int[] nums){
    int i = nums.length -1; // start from the end

    while(i > 0 && nums[i-1] >= nums[i]){
        i--;
    }

    if(i==0){
        reverse(nums,0,nums.length -1 );
    }else{

        // found the first one not in order 
        int j = i;

        // found just bigger one
        while(j < nums.length && nums[j] > nums[i-1]){
            j++;
        }
        //swap(nums[i-1],nums[j-1]);
        int tmp = nums[i-1];
        nums[i-1] = nums[j-1];
        nums[j-1] = tmp;
        reverse(nums,i,nums.length-1);  
    }
}

// reverse the sequence
void reverse(int[] arr,int start, int end){
    int tmp;
    for(int i = 0; i <= (end - start)/2; i++ ){
        tmp = arr[start + i];
        arr[start + i] = arr[end - i];
        arr[end - i ] = tmp;
    }
}
0

Визуальное представление рекурсивного решения из 3 предметов: http://www.docdroid.net/ea0s/generatepermutations.pdf.html

Разбивка:

  • Для массива из двух элементов существуют две перестановки:
    • Исходный массив и
    • Два элемента заменены
  • Для массива из трех элементов существует шесть перестановок:
    • Перестановки нижних двух элементов, затем
    • Сменить 1-й и 2-й позиции и перестановки нижнего двух элементов
    • Обмен 1-го и 3-го элементов и перестановки нижних двух элементов.
    • По сути, каждый из элементов получает свой шанс в первом слоте

Ещё вопросы

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