Вопрос-ответ

Permutation of array

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

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

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 хороши, но мне это не слишком помогает.

Переведено автоматически
Ответ 1

Вот как вы можете напечатать все перестановки в 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;
}
}

Вот полный код iterator . Конструктор принимает массив объектов и преобразует их в массив целых чисел с помощью 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 перестановки.

Ответ 2

Если вы используете 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's elements
} while(std::next_permutation(a, a+size));
Ответ 3

Вот реализация перестановки в Java:

Перестановка - Java

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

Редактировать: код вставлен ниже для защиты от потери связи:

// 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

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

Алгоритм Heap генерирует все возможные перестановки n объектов. Впервые он был предложен Б. Р. Хипом в 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, ',');
}
java algorithm