Suponiendo que estamos hablando de un orden lexicográfico sobre los valores que se permuta, hay dos enfoques generales que puede utilizar:
- transformar una permutación de los elementos a la siguiente permutación (como publicó ShreevatsaR), o
- Calcule directamente la
n
permutación th, mientras cuenta n
desde 0 hacia arriba.
Para aquellos (como yo ;-) que no hablan c ++ como nativos, el enfoque 1 se puede implementar a partir del siguiente pseudocódigo, asumiendo la indexación de base cero de una matriz con índice cero a la "izquierda" (sustituyendo alguna otra estructura , como una lista, se "deja como ejercicio" ;-):
1. scan the array from right-to-left (indices descending from N-1 to 0)
1.1. if the current element is less than its right-hand neighbor,
call the current element the pivot,
and stop scanning
1.2. if the left end is reached without finding a pivot,
reverse the array and return
(the permutation was the lexicographically last, so its time to start over)
2. scan the array from right-to-left again,
to find the rightmost element larger than the pivot
(call that one the successor)
3. swap the pivot and the successor
4. reverse the portion of the array to the right of where the pivot was found
5. return
Aquí hay un ejemplo que comienza con una permutación actual de CADB:
1. scanning from the right finds A as the pivot in position 1
2. scanning again finds B as the successor in position 3
3. swapping pivot and successor gives CBDA
4. reversing everything following position 1 (i.e. positions 2..3) gives CBAD
5. CBAD is the next permutation after CADB
Para el segundo enfoque (cálculo directo de la n
permutación-ésima), recuerde que hay N!
permutaciones de N
elementos. Por lo tanto, si está permutando N
elementos, las primeras (N-1)!
permutaciones deben comenzar con el elemento más pequeño, las siguientes (N-1)!
permutaciones deben comenzar con el segundo más pequeño, y así sucesivamente. Esto conduce al siguiente enfoque recursivo (nuevamente en pseudocódigo, numerando las permutaciones y posiciones desde 0):
To find permutation x of array A, where A has N elements:
0. if A has one element, return it
1. set p to ( x / (N-1)! ) mod N
2. the desired permutation will be A[p] followed by
permutation ( x mod (N-1)! )
of the elements remaining in A after position p is removed
Entonces, por ejemplo, la permutación 13 de ABCD se encuentra de la siguiente manera:
perm 13 of ABCD: {p = (13 / 3!) mod 4 = (13 / 6) mod 4 = 2
C followed by perm 1 of ABD {because 13 mod 3! = 13 mod 6 = 1}
perm 1 of ABD: {p = (1 / 2!) mod 3 = (1 / 2) mod 2 = 0
A followed by perm 1 of BD {because 1 mod 2! = 1 mod 2 = 1}
perm 1 of BD: {p = (1 / 1!) mod 2 = (1 / 1) mod 2 = 1
D followed by perm 0 of B {because 1 mod 1! = 1 mod 1 = 0}
B (because there's only one element)
DB
ADB
CADB
Por cierto, la "eliminación" de elementos se puede representar mediante una matriz paralela de valores booleanos que indica qué elementos todavía están disponibles, por lo que no es necesario crear una nueva matriz en cada llamada recursiva.
Entonces, para iterar a través de las permutaciones de ABCD, simplemente cuente de 0 a 23 (4! -1) y calcule directamente la permutación correspondiente.