Permutación de matriz

Resuelto asked hace 14 años • 14 respuestas

Por ejemplo tengo esta matriz:

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

Necesito una lista de todas las permutaciones de modo que si una es así, {3,2,1,4,6}las demás no deben ser iguales. Sé que si la longitud de la matriz es n , ¡ entonces hay n! posibles combinaciones. ¿Cómo se puede escribir este algoritmo?

Actualización: gracias, pero necesito un algoritmo de pseudocódigo como:

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

Solo algoritmo. Sí, las funciones API son buenas, pero no me ayudan demasiado.

 avatar May 27 '10 17:05
Aceptado

Así es como puedes imprimir todas las permutaciones en 10 líneas de código:

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);
    }
}

Toma el primer elemento de una matriz (k = 0) y lo intercambia con cualquier elemento (i) de la matriz. Luego aplica recursivamente la permutación en la matriz comenzando con el segundo elemento. De esta manera obtienes todas las permutaciones que comienzan con el i-ésimo elemento. La parte complicada es que después de la llamada recursiva debes intercambiar el i-ésimo elemento con el primer elemento; de lo contrario, podrías obtener valores repetidos en el primer lugar. Al volver a intercambiarlo, restauramos el orden de los elementos (básicamente se retrocede).

Iteradores y Extensión al caso de valores repetidos.

El inconveniente del algoritmo anterior es que es recursivo y no funciona bien con los iteradores. Otro problema es que si permite elementos repetidos en su entrada, no funcionará como está.

Por ejemplo, dada la entrada [3,3,4,4] todas las permutaciones posibles (sin repeticiones) son

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

(si simplemente aplica permutela función anterior obtendrá [3,3,4,4] cuatro veces, y esto no es lo que naturalmente desea ver en este caso; y el número de tales permutaciones es 4!/(2! *2!)=6)

Es posible modificar el algoritmo anterior para manejar este caso, pero no quedará bien. Afortunadamente, existe un algoritmo mejor (lo encontré aquí ) que maneja valores repetidos y no es recursivo.

En primer lugar, tenga en cuenta que la permutación de una matriz de cualquier objeto se puede reducir a permutaciones de números enteros enumerándolos en cualquier orden.

Para obtener permutaciones de una matriz de números enteros, comience con una matriz ordenada en orden ascendente. Tu 'objetivo' es hacerlo descender. Para generar la siguiente permutación, intenta encontrar el primer índice desde abajo donde la secuencia no es descendente y mejora el valor en ese índice mientras cambia el orden del resto de la cola de descendente a ascendente en este caso.

Aquí está el núcleo del algoritmo:

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

Aquí está el código completo del iterador. Constructor acepta una matriz de objetos y los asigna a una matriz de números enteros usando 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() {

    }
}

Uso/prueba:

    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);

Imprime todas 7!/(2!*3!*2!)=210las permutaciones.

Yevgen Yampolskiy avatar Jan 21 '2013 17:01 Yevgen Yampolskiy

Si estás usando C++, puedes usar std::next_permutationdesde el <algorithm>archivo de encabezado:

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));
reko_t avatar May 27 '2010 10:05 reko_t

Aquí hay una implementación de la Permutación en Java:

Permutación - Java

¡Deberías comprobarlo!

Editar: código pegado a continuación para proteger contra la muerte del enlace:

// 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);
      }
   }
}
Mr.Expert avatar May 27 '2010 10:05 Mr.Expert