n 개 요소의 순열을 설명하려면 첫 번째 요소가 끝나는 위치에 대해 n 개의 가능성이 있으므로 0에서 n-1 사이의 숫자로이를 설명 할 수 있습니다. 다음 요소가 끝나는 위치에 대해 n-1 개의 남은 가능성이 있으므로 0에서 n-2 사이의 숫자로이를 설명 할 수 있습니다.
당신이 n 개의 숫자를 가질 때까지 등등.
N = 5에 대한 예를 들어, 제공 순열 고려 abcde
에를 caebd
.
a
첫 번째 요소 인은 두 번째 위치에서 끝나므로 인덱스 1을 할당합니다 .
b
결국 인덱스 3 인 네 번째 위치에 있지만 나머지 세 번째 위치이므로 2를 할당합니다 .
c
항상 0 인 첫 번째 남은 위치에서 끝납니다 .
d
마지막 남은 위치에서 끝납니다 (남은 위치 2 개 중)는 1 입니다.
e
0 에서 색인 된 유일한 나머지 위치에서 끝납니다 .
따라서 인덱스 시퀀스는 {1, 2, 0, 1, 0} 입니다.
예를 들어 이진수에서 'xyz'는 z + 2y + 4x를 의미합니다. 십진수
의 경우 z + 10y + 100x입니다. 각 숫자에 가중치를 곱하고 결과가 합산됩니다. 가중치의 명백한 패턴은 물론 가중치가 w = b ^ k이고, b는 숫자의 밑이고 k는 숫자의 인덱스입니다. (나는 항상 오른쪽에서 숫자를 세고 맨 오른쪽 숫자는 인덱스 0에서 시작합니다. 마찬가지로 '첫 번째'숫자에 대해 말할 때 가장 오른쪽을 의미합니다.)
이유 자리에 대한 가중치는이 패턴을 따를 이유는 K 0에서 숫자에 의해 표현 될 수있는 가장 높은 숫자 만 자리 K + 1을 사용하여 표현 될 수있는 최소 수보다 정확히 1 낮아야한다는 것이다. 2 진수에서 0111은 1000보다 낮은 값이어야합니다. 10 진수에서 099999는 100000보다 낮은 값이어야합니다.
변수 기반으로 인코딩
다음 숫자 사이의 간격이 정확히 1 인 것이 중요한 규칙입니다. 이것을 깨닫고, 우리는 변수 기반 숫자로 인덱스 시퀀스를 나타낼 수 있습니다. 각 숫자의 기준은 해당 숫자에 대해 서로 다른 가능성의 양입니다. 십진수의 경우 각 숫자에는 10 개의 가능성이 있으며, 시스템의 경우 맨 오른쪽 숫자에는 1 개의 가능성이 있고 맨 왼쪽에는 n 개의 가능성이 있습니다. 그러나 가장 오른쪽 숫자 (순서의 마지막 숫자)는 항상 0이므로 생략합니다. 즉, 2에서 n까지의 염기가 남습니다. 일반적으로 k 번째 자리는 b [k] = k + 2입니다. 자리 k에 허용되는 가장 높은 값은 h [k] = b [k]-1 = k + 1입니다.
자릿수의 가중치 w [k]에 대한 규칙은 i = 0에서 i = k로가는 h [i] * w [i]의 합이 1 * w [k + 1]과 같아야합니다. 반복적으로 설명하면 w [k + 1] = w [k] + h [k] * w [k] = w [k] * (h [k] + 1). 첫 번째 가중치 w [0]은 항상 1이어야합니다. 여기에서 시작하여 다음 값이 있습니다.
k h[k] w[k]
0 1 1
1 2 2
2 3 6
3 4 24
... ... ...
n-1 n n!
(일반적인 관계 w [k-1] = k!는 귀납법으로 쉽게 증명됩니다.)
시퀀스를 변환하여 얻은 숫자는 s [k] * w [k]의 합이되며 k는 0에서 n-1로 이어집니다. 여기서 s [k]는 시퀀스의 k 번째 (가장 오른쪽, 0에서 시작) 요소입니다. 예를 들어, 앞서 언급 한 것처럼 가장 오른쪽 요소가 제거 된 {1, 2, 0, 1, 0}을 가져옵니다 : {1, 2, 0, 1} . 우리의 합은 1 * 1 + 0 * 2 + 2 * 6 + 1 * 24 = 37 입니다.
모든 인덱스에 대해 최대 위치를 취하면 {4, 3, 2, 1, 0}이되고 119로 변환됩니다. 숫자 인코딩의 가중치는 건너 뛰지 않도록 선택되었으므로 모든 숫자, 0에서 119까지의 모든 숫자가 유효합니다. 정확히 120 개가 있습니다. 이 예에서 n = 5 인 경우 정확히 다른 순열의 수입니다. 따라서 인코딩 된 숫자가 가능한 모든 순열을 완전히 지정하는 것을 볼 수 있습니다.
가변 기반
디코딩에서 디코딩은 이진 또는 10 진수로 변환하는 것과 유사합니다. 일반적인 알고리즘은 다음과 같습니다.
int number = 42;
int base = 2;
int[] bits = new int[n];
for (int k = 0; k < bits.Length; k++)
{
bits[k] = number % base;
number = number / base;
}
가변 염기 번호의 경우 :
int n = 5;
int number = 37;
int[] sequence = new int[n - 1];
int base = 2;
for (int k = 0; k < sequence.Length; k++)
{
sequence[k] = number % base;
number = number / base;
base++; // b[k+1] = b[k] + 1
}
이 올바르게 {1, 2, 0, 1}에 대한 우리의 37 다시 디코딩 ( sequence
것 {1, 0, 2, 1}
이 코드 예제에서, 그러나 어떤 ...만큼 인덱스로 적절하게). 원래 시퀀스 {1, 2, 0, 1, 0}을 되돌리려면 오른쪽 끝에 0을 추가하기 만하면됩니다 (마지막 요소는 항상 새 위치에 대해 하나의 가능성 만 있음을 기억하십시오).
인덱스 시퀀스
를 사용하여 목록 순열 아래 알고리즘을 사용하여 특정 인덱스 시퀀스에 따라 목록을 순열 할 수 있습니다. 불행히도 O (n²) 알고리즘입니다.
int n = 5;
int[] sequence = new int[] { 1, 2, 0, 1, 0 };
char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];
bool[] set = new bool[n];
for (int i = 0; i < n; i++)
{
int s = sequence[i];
int remainingPosition = 0;
int index;
// Find the s'th position in the permuted list that has not been set yet.
for (index = 0; index < n; index++)
{
if (!set[index])
{
if (remainingPosition == s)
break;
remainingPosition++;
}
}
permuted[index] = list[i];
set[index] = true;
}
순열의 일반적인 표현
일반적으로 우리가 한 것처럼 직관적이지 않게 순열을 표현하는 것이 아니라 순열이 적용된 후 각 요소의 절대 위치로 간단히 표현할 수 있습니다. 우리의 예는 {1, 2, 0, 1, 0}의 abcde
행은 caebd
일반적으로 {1, 3, 0, 4, 2}로 표시된다. 0에서 4 (또는 일반적으로 0에서 n-1)의 각 인덱스는이 표현에서 정확히 한 번 발생합니다.
이 형식으로 순열을 적용하는 것은 쉽습니다.
int[] permutation = new int[] { 1, 3, 0, 4, 2 };
char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];
for (int i = 0; i < n; i++)
{
permuted[permutation[i]] = list[i];
}
반전은 매우 유사합니다.
for (int i = 0; i < n; i++)
{
list[i] = permuted[permutation[i]];
}
표현에서 공통 표현으로 변환
우리의 알고리즘을 인덱스 시퀀스를 사용하여 목록을 순열하고 ID 순열 {0, 1, 2, ..., n-1}에 적용하면 일반적인 형태로 표현되는 역 순열. (이 예에서는 {2, 0, 4, 1, 3} ).
반전되지 않은 사전 돌연변이를 얻기 위해 방금 보여준 순열 알고리즘을 적용합니다.
int[] identity = new int[] { 0, 1, 2, 3, 4 };
int[] inverted = { 2, 0, 4, 1, 3 };
int[] normal = new int[n];
for (int i = 0; i < n; i++)
{
normal[identity[i]] = list[i];
}
또는 역 순열 알고리즘을 사용하여 순열을 직접 적용 할 수 있습니다.
char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];
int[] inverted = { 2, 0, 4, 1, 3 };
for (int i = 0; i < n; i++)
{
permuted[i] = list[inverted[i]];
}
일반적인 형식의 순열을 처리하는 모든 알고리즘은 O (n)이고, 형식으로 순열을 적용하는 것은 O (n²)입니다. 순열을 여러 번 적용해야하는 경우 먼저 일반 표현으로 변환하십시오.