Supondo que estejamos falando sobre ordem lexicográfica sobre os valores que estão sendo permutados, existem duas abordagens gerais que você pode usar:
- transformar uma permutação dos elementos para a próxima permutação (como ShreevatsaR postou), ou
- calcular diretamente a
n
ésima permutação, enquanto conta n
de 0 para cima.
Para aqueles (como eu ;-) que não falam c ++ como nativos, a abordagem 1 pode ser implementada a partir do seguinte pseudocódigo, assumindo a indexação baseada em zero de uma matriz com índice zero à "esquerda" (substituindo alguma outra estrutura , como uma lista, é "deixado como um exercício" ;-):
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
Aqui está um exemplo começando com uma permutação atual 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 a segunda abordagem (cálculo direto da n
ésima permutação), lembre-se de que existem N!
permutações de N
elementos. Portanto, se você estiver permutando N
elementos, as primeiras (N-1)!
permutações devem começar com o menor elemento, as próximas (N-1)!
permutações devem começar com o segundo menor e assim por diante. Isso leva à seguinte abordagem recursiva (novamente em pseudocódigo, numerando as permutações e posições de 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
Assim, por exemplo, a 13ª permutação de ABCD é encontrada da seguinte forma:
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
A propósito, a "remoção" de elementos pode ser representada por um array paralelo de booleanos que indica quais elementos ainda estão disponíveis, portanto, não é necessário criar um novo array a cada chamada recursiva.
Portanto, para iterar pelas permutações de ABCD, basta contar de 0 a 23 (4! -1) e calcular diretamente a permutação correspondente.