[algorithm] 빠른 순열-> 숫자-> 순열 매핑 알고리즘
n 개의 요소가 있습니다. 예를 들어, 7 개의 요소, 1234567이라고합시다. 저는 7 개가 있다는 것을 압니다! =이 7 개 요소에 대해 5040 개의 순열이 가능합니다.
두 가지 기능으로 구성된 빠른 알고리즘을 원합니다.
f (number)는 0에서 5039 사이의 숫자를 고유 한 순열에 매핑합니다.
f ‘(순열)은 순열을 생성 된 숫자로 다시 매핑합니다.
각 순열에 고유 한 번호가있는 경우 숫자와 순열 간의 대응에 대해서는 신경 쓰지 않습니다.
예를 들어, 저는
f(0) = '1234567'
f'('1234567') = 0
떠오르는 가장 빠른 알고리즘은 모든 순열을 열거하고 양방향으로 룩업 테이블을 생성하여 테이블이 생성되면 f (0)은 O (1)이되고 f ( ‘1234567’)는 문자열 조회. 그러나 이것은 특히 n이 커질 때 메모리가 부족합니다.
누구든지 메모리 단점없이 빠르게 작동하는 다른 알고리즘을 제안 할 수 있습니까?
답변
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²)입니다. 순열을 여러 번 적용해야하는 경우 먼저 일반 표현으로 변환하십시오.
답변
O (n) 알고리즘을 찾았습니다. 여기에 간단한 설명이 있습니다. http://antoinecomeau.blogspot.ca/2014/07/mapping-between-permutations-and.html
public static int[] perm(int n, int k)
{
int i, ind, m=k;
int[] permuted = new int[n];
int[] elems = new int[n];
for(i=0;i<n;i++) elems[i]=i;
for(i=0;i<n;i++)
{
ind=m%(n-i);
m=m/(n-i);
permuted[i]=elems[ind];
elems[ind]=elems[n-i-1];
}
return permuted;
}
public static int inv(int[] perm)
{
int i, k=0, m=1;
int n=perm.length;
int[] pos = new int[n];
int[] elems = new int[n];
for(i=0;i<n;i++) {pos[i]=i; elems[i]=i;}
for(i=0;i<n-1;i++)
{
k+=m*pos[perm[i]];
m=m*(n-i);
pos[elems[n-i-1]]=pos[perm[i]];
elems[pos[perm[i]]]=elems[n-i-1];
}
return k;
}
답변
복잡성은 n * log (n)로 낮출 수 있습니다. fxtbook의 섹션 10.1.1 ( “Lehmer 코드 (반전 테이블)”, p.232ff) 참조 :
http://www.jjj.de/fxt/ #fxtbook
빠른 방법에 대해서는 섹션 10.1.1.1 ( “대형 배열을 사용한 계산”235 페이지)으로 건너 뜁니다. (GPLed, C ++) 코드는 동일한 웹 페이지에 있습니다.
답변
문제 해결됨. 그러나 몇 년이 지난 후에도 여전히 솔루션이 필요한지 잘 모르겠습니다. LOL, 방금이 사이트에 가입 했으므로 Java Permutation Class를 확인하십시오. 인덱스를 기반으로 기호 순열을 얻거나 기호 순열을 제공 한 다음 인덱스를 얻을 수 있습니다.
내 Premutation 클래스는 다음과 같습니다.
/**
****************************************************************************************************************
* Copyright 2015 Fred Pang fred@pnode.com
****************************************************************************************************************
* A complete list of Permutation base on an index.
* Algorithm is invented and implemented by Fred Pang fred@pnode.com
* Created by Fred Pang on 18/11/2015.
****************************************************************************************************************
* LOL this is my first Java project. Therefore, my code is very much like C/C++. The coding itself is not
* very professional. but...
*
* This Permutation Class can be use to generate a complete list of all different permutation of a set of symbols.
* nPr will be n!/(n-r)!
* the user can input n = the number of items,
* r = the number of slots for the items,
* provided n >= r
* and a string of single character symbols
*
* the program will generate all possible permutation for the condition.
*
* Say if n = 5, r = 3, and the string is "12345", it will generate sll 60 different permutation of the set
* of 3 character strings.
*
* The algorithm I used is base on a bin slot.
* Just like a human or simply myself to generate a permutation.
*
* if there are 5 symbols to chose from, I'll have 5 bin slot to indicate which symbol is taken.
*
* Note that, once the Permutation object is initialized, or after the constructor is called, the permutation
* table and all entries are defined, including an index.
*
* eg. if pass in value is 5 chose 3, and say the symbol string is "12345"
* then all permutation table is logically defined (not physically to save memory).
* It will be a table as follows
* index output
* 0 123
* 1 124
* 2 125
* 3 132
* 4 134
* 5 135
* 6 143
* 7 145
* : :
* 58 542
* 59 543
*
* all you need to do is call the "String PermGetString(int iIndex)" or the "int[] PermGetIntArray(int iIndex)"
* function or method with an increasing iIndex, starting from 0 to getiMaxIndex() - 1. It will return the string
* or the integer array corresponding to the index.
*
* Also notice that in the input string is "12345" of position 01234, and the output is always in accenting order
* this is how the permutation is generated.
*
* ***************************************************************************************************************
* ==== W a r n i n g ====
* ***************************************************************************************************************
*
* There is very limited error checking in this class
*
* Especially the int PermGetIndex(int[] iInputArray) method
* if the input integer array contains invalid index, it WILL crash the system
*
* the other is the string of symbol pass in when the object is created, not sure what will happen if the
* string is invalid.
* ***************************************************************************************************************
*
*/
public class Permutation
{
private boolean bGoodToGo = false; // object status
private boolean bNoSymbol = true;
private BinSlot slot; // a bin slot of size n (input)
private int nTotal; // n number for permutation
private int rChose; // r position to chose
private String sSymbol; // character string for symbol of each choice
private String sOutStr;
private int iMaxIndex; // maximum index allowed in the Get index function
private int[] iOutPosition; // output array
private int[] iDivisorArray; // array to do calculation
public Permutation(int inCount, int irCount, String symbol)
{
if (inCount >= irCount)
{
// save all input values passed in
this.nTotal = inCount;
this.rChose = irCount;
this.sSymbol = symbol;
// some error checking
if (inCount < irCount || irCount <= 0)
return; // do nothing will not set the bGoodToGo flag
if (this.sSymbol.length() >= inCount)
{
bNoSymbol = false;
}
// allocate output storage
this.iOutPosition = new int[this.rChose];
// initialize the bin slot with the right size
this.slot = new BinSlot(this.nTotal);
// allocate and initialize divid array
this.iDivisorArray = new int[this.rChose];
// calculate default values base on n & r
this.iMaxIndex = CalPremFormula(this.nTotal, this.rChose);
int i;
int j = this.nTotal - 1;
int k = this.rChose - 1;
for (i = 0; i < this.rChose; i++)
{
this.iDivisorArray[i] = CalPremFormula(j--, k--);
}
bGoodToGo = true; // we are ready to go
}
}
public String PermGetString(int iIndex)
{
if (!this.bGoodToGo) return "Error: Object not initialized Correctly";
if (this.bNoSymbol) return "Error: Invalid symbol string";
if (!this.PermEvaluate(iIndex)) return "Invalid Index";
sOutStr = "";
// convert string back to String output
for (int i = 0; i < this.rChose; i++)
{
String sTempStr = this.sSymbol.substring(this.iOutPosition[i], iOutPosition[i] + 1);
this.sOutStr = this.sOutStr.concat(sTempStr);
}
return this.sOutStr;
}
public int[] PermGetIntArray(int iIndex)
{
if (!this.bGoodToGo) return null;
if (!this.PermEvaluate(iIndex)) return null ;
return this.iOutPosition;
}
// given an int array, and get the index back.
//
// ====== W A R N I N G ======
//
// there is no error check in the array that pass in
// if any invalid value in the input array, it can cause system crash or other unexpected result
//
// function pass in an int array generated by the PermGetIntArray() method
// then return the index value.
//
// this is the reverse of the PermGetIntArray()
//
public int PermGetIndex(int[] iInputArray)
{
if (!this.bGoodToGo) return -1;
return PermDoReverse(iInputArray);
}
public int getiMaxIndex() {
return iMaxIndex;
}
// function to evaluate nPr = n!/(n-r)!
public int CalPremFormula(int n, int r)
{
int j = n;
int k = 1;
for (int i = 0; i < r; i++, j--)
{
k *= j;
}
return k;
}
// PermEvaluate function (method) base on an index input, evaluate the correspond permuted symbol location
// then output it to the iOutPosition array.
//
// In the iOutPosition[], each array element corresponding to the symbol location in the input string symbol.
// from location 0 to length of string - 1.
private boolean PermEvaluate(int iIndex)
{
int iCurrentIndex;
int iCurrentRemainder;
int iCurrentValue = iIndex;
int iCurrentOutSlot;
int iLoopCount;
if (iIndex >= iMaxIndex)
return false;
this.slot.binReset(); // clear bin content
iLoopCount = 0;
do {
// evaluate the table position
iCurrentIndex = iCurrentValue / this.iDivisorArray[iLoopCount];
iCurrentRemainder = iCurrentValue % this.iDivisorArray[iLoopCount];
iCurrentOutSlot = this.slot.FindFreeBin(iCurrentIndex); // find an available slot
if (iCurrentOutSlot >= 0)
this.iOutPosition[iLoopCount] = iCurrentOutSlot;
else return false; // fail to find a slot, quit now
this.slot.setStatus(iCurrentOutSlot); // set the slot to be taken
iCurrentValue = iCurrentRemainder; // set new value for current value.
iLoopCount++; // increase counter
} while (iLoopCount < this.rChose);
// the output is ready in iOutPosition[]
return true;
}
//
// this function is doing the reverse of the permutation
// the input is a permutation and will find the correspond index value for that entry
// which is doing the opposit of the PermEvaluate() method
//
private int PermDoReverse(int[] iInputArray)
{
int iReturnValue = 0;
int iLoopIndex;
int iCurrentValue;
int iBinLocation;
this.slot.binReset(); // clear bin content
for (iLoopIndex = 0; iLoopIndex < this.rChose; iLoopIndex++)
{
iCurrentValue = iInputArray[iLoopIndex];
iBinLocation = this.slot.BinCountFree(iCurrentValue);
this.slot.setStatus(iCurrentValue); // set the slot to be taken
iReturnValue = iReturnValue + iBinLocation * this.iDivisorArray[iLoopIndex];
}
return iReturnValue;
}
/*******************************************************************************************************************
*******************************************************************************************************************
* Created by Fred on 18/11/2015. fred@pnode.com
*
* *****************************************************************************************************************
*/
private static class BinSlot
{
private int iBinSize; // size of array
private short[] eStatus; // the status array must have length iBinSize
private BinSlot(int iBinSize)
{
this.iBinSize = iBinSize; // save bin size
this.eStatus = new short[iBinSize]; // llocate status array
}
// reset the bin content. no symbol is in use
private void binReset()
{
// reset the bin's content
for (int i = 0; i < this.iBinSize; i++) this.eStatus[i] = 0;
}
// set the bin position as taken or the number is already used, cannot be use again.
private void setStatus(int iIndex) { this.eStatus[iIndex]= 1; }
//
// to search for the iIndex th unused symbol
// this is important to search through the iindex th symbol
// because this is how the table is setup. (or the remainder means)
// note: iIndex is the remainder of the calculation
//
// for example:
// in a 5 choose 3 permutation symbols "12345",
// the index 7 item (count starting from 0) element is "1 4 3"
// then comes the index 8, 8/12 result 0 -> 0th symbol in symbol string = '1'
// remainder 8. then 8/3 = 2, now we need to scan the Bin and skip 2 unused bins
// current the bin looks 0 1 2 3 4
// x o o o o x -> in use; o -> free only 0 is being used
// s s ^ skipped 2 bins (bin 1 and 2), we get to bin 3
// and bin 3 is the bin needed. Thus symbol "4" is pick
// in 8/3, there is a remainder 2 comes in this function as 2/1 = 2, now we have to pick the empty slot
// for the new 2.
// the bin now looks 0 1 2 3 4
// x 0 0 x 0 as bin 3 was used by the last value
// s s ^ we skip 2 free bins and the next free bin is bin 4
// therefor the symbol "5" at the symbol array is pick.
//
// Thus, for index 8 "1 4 5" is the symbols.
//
//
private int FindFreeBin(int iIndex)
{
int j = iIndex;
if (j < 0 || j > this.iBinSize) return -1; // invalid index
for (int i = 0; i < this.iBinSize; i++)
{
if (this.eStatus[i] == 0) // is it used
{
// found an empty slot
if (j == 0) // this is a free one we want?
return i; // yes, found and return it.
else // we have to skip this one
j--; // else, keep looking and count the skipped one
}
}
assert(true); // something is wrong
return -1; // fail to find the bin we wanted
}
//
// this function is to help the PermDoReverse() to find out what is the corresponding
// value during should be added to the index value.
//
// it is doing the opposite of int FindFreeBin(int iIndex) method. You need to know how this
// FindFreeBin() works before looking into this function.
//
private int BinCountFree(int iIndex)
{
int iRetVal = 0;
for (int i = iIndex; i > 0; i--)
{
if (this.eStatus[i-1] == 0) // it is free
{
iRetVal++;
}
}
return iRetVal;
}
}
}
// End of file - Permutation.java
여기에 수업 사용 방법을 보여주는 메인 클래스가 있습니다.
/*
* copyright 2015 Fred Pang
*
* This is the main test program for testing the Permutation Class I created.
* It can be use to demonstrate how to use the Permutation Class and its methods to generate a complete
* list of a permutation. It also support function to get back the index value as pass in a permutation.
*
* As you can see my Java is not very good. :)
* This is my 1st Java project I created. As I am a C/C++ programmer for years.
*
* I still have problem with the Scanner class and the System class.
* Note that there is only very limited error checking
*
*
*/
import java.util.Scanner;
public class Main
{
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args)
{
Permutation perm; // declear the object
String sOutString = "";
int nCount;
int rCount;
int iMaxIndex;
// Get user input
System.out.println("Enter n: ");
nCount = scanner.nextInt();
System.out.println("Enter r: ");
rCount = scanner.nextInt();
System.out.println("Enter Symbol: ");
sOutString = scanner.next();
if (sOutString.length() < rCount)
{
System.out.println("String too short, default to numbers");
sOutString = "";
}
// create object with user requirement
perm = new Permutation(nCount, rCount, sOutString);
// and print the maximum count
iMaxIndex = perm.getiMaxIndex();
System.out.println("Max count is:" + iMaxIndex);
if (!sOutString.isEmpty())
{
for (int i = 0; i < iMaxIndex; i++)
{ // print out the return permutation symbol string
System.out.println(i + " " + perm.PermGetString(i));
}
}
else
{
for (int i = 0; i < iMaxIndex; i++)
{
System.out.print(i + " ->");
// Get the permutation array
int[] iTemp = perm.PermGetIntArray(i);
// print out the permutation
for (int j = 0; j < rCount; j++)
{
System.out.print(' ');
System.out.print(iTemp[j]);
}
// to verify my PermGetIndex() works. :)
if (perm.PermGetIndex(iTemp)== i)
{
System.out.println(" .");
}
else
{ // oops something is wrong :(
System.out.println(" ***************** F A I L E D *************************");
assert(true);
break;
}
}
}
}
}
//
// End of file - Main.java
즐기세요. 🙂
답변
각 요소는 7 개의 위치 중 하나에있을 수 있습니다. 한 요소의 위치를 설명하려면 3 비트가 필요합니다. 즉, 모든 요소의 위치를 32 비트 값으로 저장할 수 있습니다. 이 표현은 모든 요소가 동일한 위치에 있도록 할 수 있기 때문에 효율성과는 거리가 멀지 만 비트 마스킹이 합리적으로 빨라야한다고 생각합니다.
그러나 8 개 이상의 위치에서는 더 멋진 것이 필요합니다.
답변
이것은 J 의 내장 함수입니다 .
A. 1 2 3 4 5 6 7
0
0 A. 1 2 3 4 5 6 7
1 2 3 4 5 6 7
?!7
5011
5011 A. 1 2 3 4 5 6 7
7 6 4 5 1 3 2
A. 7 6 4 5 1 3 2
5011
답변
재귀 알고리즘을 사용하여 순열을 인코딩 할 수 있습니다. N- 순열 (숫자 {0, .., N-1}의 일부 순서)이 {x, …} 형식 인 경우 x + N * (N-1)의 인코딩으로 인코딩합니다. -숫자 {0, N-1}-{x}에서 “…”로 표시되는 순열. 한입처럼 들리지만 다음은 몇 가지 코드입니다.
// perm[0]..perm[n-1] must contain the numbers in {0,..,n-1} in any order.
int permToNumber(int *perm, int n) {
// base case
if (n == 1) return 0;
// fix up perm[1]..perm[n-1] to be a permutation on {0,..,n-2}.
for (int i = 1; i < n; i++) {
if (perm[i] > perm[0]) perm[i]--;
}
// recursively compute
return perm[0] + n * permToNumber(perm + 1, n - 1);
}
// number must be >=0, < n!
void numberToPerm(int number, int *perm, int n) {
if (n == 1) {
perm[0] = 0;
return;
}
perm[0] = number % n;
numberToPerm(number / n, perm + 1, n - 1);
// fix up perm[1] .. perm[n-1]
for (int i = 1; i < n; i++) {
if (perm[i] >= perm[0]) perm[i]++;
}
}
이 알고리즘은 O (n ^ 2)입니다. 누군가가 O (n) 알고리즘을 가지고 있다면 보너스 포인트.