빠른 순열-> 숫자-> 순열 매핑 알고리즘


113

n 개의 요소가 있습니다. 예를 들어, 7 개의 요소, 1234567이라고합시다. 저는 7 개가 있다는 것을 압니다! =이 7 개 요소에 대해 5040 개의 순열이 가능합니다.

두 가지 기능으로 구성된 빠른 알고리즘을 원합니다.

f (number)는 0에서 5039 사이의 숫자를 고유 한 순열에 매핑합니다.

f '(순열)은 순열을 생성 된 숫자로 다시 매핑합니다.

각 순열에 고유 한 번호가있는 경우 숫자와 순열 간의 대응에 대해서는 신경 쓰지 않습니다.

예를 들어, 저는

f(0) = '1234567'
f'('1234567') = 0

떠오르는 가장 빠른 알고리즘은 모든 순열을 열거하고 양방향으로 룩업 테이블을 생성하여 테이블이 생성되면 f (0)은 O (1)이되고 f ( '1234567')는 문자열 조회. 그러나 이것은 특히 n이 커질 때 메모리가 부족합니다.

누구든지 메모리 단점없이 빠르게 작동하는 다른 알고리즘을 제안 할 수 있습니까?


아래의 알고리즘은 매우 포괄적이지만 가장 빠른 알고리즘이 조회 테이블임을 올바르게 지적합니다. 물론 시스템 및 플랫폼에 따라 다르지만 실제로 '그 정도'메모리에 대해 이야기하는 것은 아닙니다. 그러나 조회 테이블이 충분하고 이것이 실제 응용 프로그램이라면 그것을 사용하십시오. 빠르고 간단합니다!
Kirk Broadhurst

14
당신은 그렇게 말하지만 n은 그것이 어리석기 위해 매우 커질 필요가 없습니다. 12 개 요소, 12 개! 순열은 479,001,600입니다. 그것은 큰 조회 테이블입니다!
ijw

다른 게시물로 혼동하지 마십시오 .n을 다른 의미로 사용하십시오. 일부 n은 문자열 길이를 나타내고 일부 n은 가능한 순열 수를 나타냅니다. 큰 O 개념을 맹목적으로 비교하지 마십시오. - - 늦은 올 사람은 경고한다 -
把友情留在无盐

답변:


157

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 입니다.
  • e0 에서 색인 된 유일한 나머지 위치에서 끝납니다 .

따라서 인덱스 시퀀스는 {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²)입니다. 순열을 여러 번 적용해야하는 경우 먼저 일반 표현으로 변환하십시오.


6
"인덱스 시퀀스를 사용하여 목록 순열"에서 2 차 알고리즘을 언급합니다. n은 아마도 매우 작을 것이기 때문에 이것은 확실히 괜찮습니다. 이것은 주문 통계 트리 ( pine.cs.yale.edu/pinewiki/OrderStatisticsTree )를 통해 "쉽게"O (nlogn)로 줄일 수 있습니다 . 즉, 처음에는 0, 1, 2 값을 포함하는 빨강-검정 트리입니다. , ..., n-1 및 각 노드에는 그 아래에있는 하위 항목 수가 포함됩니다. 이를 통해 O (logn) 시간에 k 번째 요소를 찾아 제거 할 수 있습니다.
Dimitris Andreou

11
이를 레머 코드라고합니다. 이 링크는 또한 그들을 잘 설명합니다. keithschwarz.com/interesting/code/?dir=factoradic-permutation
mihirg

이 알고리즘은 훌륭하지만 몇 가지 잘못된 경우를 발견했습니다. 문자열 "123"을 사용합니다. 네 번째 순열은 231이어야하지만이 알고리즘에 따르면 312가됩니다. 예를 들어 1234라고하면 네 번째 순열은 1342 여야하지만 "1423"으로 오인됩니다. 내가 잘못 발견하면 정정하십시오. 감사.
Isaac Li

@IsaacLi, 내가 맞다면 f (4) = {2, 0, 0} = 231. 그리고 f '(312) = {1, 1, 0} = 3. For 1234, f (4) = {0, 2, 0, 0} = 1342. 그리고 f '(1423) = {0, 1 1, 0} = 3.이 알고리즘은 정말 고무적입니다. OP의 오리지널 작품 인 것 같아요. 나는 그것을 한동안 연구하고 분석했습니다. 그리고 나는 그것이 옳다고 믿는다 :)
midnite 2013

"우리의 표현"에서 "공통적 인 표현"으로 변환하는 방법, {1, 2, 0, 1, 0}-> {1, 3, 0, 4, 2}? 그 반대? 가능할까요? ( O (n ^ 2)를 필요로하는 <-> 사이를 변환 하지 않음 으로써 .) "우리 스타일"과 "공통 스타일"이 변환 가능하지 않다면, 그들은 실제로 두 개의 다른 별개의 것입니까? Thanks x{1, 2, 0, 1, 0}{C, A, E, B, D}
midnite 2013

18

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

1
내가 당신의 알고리즘을 아주 잘 이해한다면. 인코딩 된 모든 가능성을 찾고 있습니다 (이 경우 n! 가능성이어야합니다). 그런 다음 인코딩 된 항목을 기반으로 숫자를 매핑합니다.
user3378649 2014-10-23

내 블로그에 간단한 설명을 추가했습니다.
Antoine Comeau 2015 년

1
이것은 매우 깔끔합니다. 오늘 저도 같은 방법을 생각해 냈지만 역으로 두 가지 과제를 생략 할 수 있다는 사실을 놓쳤습니다.
fuz

이 답변의 n은 @ user3378649가 지적했듯이 다른 답변과 동일하지 않으므로 big O 개념을 맹목적으로 비교하지 마십시오. 문자열 길이의 계승에 대한 복잡성 비율을 나타냅니다. 이 대답은 실제로 덜 효율적입니다.
把 友情 留 在 无 盐

사전 식 순서에 맞게 조정할 수 있습니까?
Gregory Morse

7

복잡성은 n * log (n)로 낮출 수 있습니다. fxtbook의 섹션 10.1.1 ( "Lehmer 코드 (반전 테이블)", p.232ff) 참조 : http://www.jjj.de/fxt/ #fxtbook 빠른 방법에 대해서는 섹션 10.1.1.1 ( "대형 배열을 사용한 계산"235 페이지)으로 건너 뜁니다. (GPLed, C ++) 코드는 동일한 웹 페이지에 있습니다.


5

문제 해결됨. 그러나 몇 년이 지난 후에도 여전히 솔루션이 필요한지 잘 모르겠습니다. 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

즐기세요. :)


4

각 요소는 7 개의 위치 중 하나에있을 수 있습니다. 한 요소의 위치를 ​​설명하려면 3 비트가 필요합니다. 즉, 모든 요소의 위치를 ​​32 비트 값으로 저장할 수 있습니다. 이 표현은 모든 요소가 동일한 위치에 있도록 할 수 있기 때문에 효율성과는 거리가 멀지 만 비트 마스킹이 합리적으로 빨라야한다고 생각합니다.

그러나 8 개 이상의 위치에서는 더 멋진 것이 필요합니다.


이것은 열거 형이 실제로 0에서 5039까지 가는지 OP가 신경 쓰지 않는다고 가정합니다. 맞죠? 괜찮다면 이것은 훌륭한 솔루션 인 것 같습니다.
Troubadour

4

이것은 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

2

재귀 알고리즘을 사용하여 순열을 인코딩 할 수 있습니다. 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) 알고리즘을 가지고 있다면 보너스 포인트.


1

정말 흥미로운 질문입니다!

모든 요소가 숫자 인 경우 문자열에서 실제 숫자로 변환하는 것을 고려할 수 있습니다. 그런 다음 모든 순열을 순서대로 정렬하고 배열에 배치 할 수 있습니다. 그 후에는 다양한 검색 알고리즘을 사용할 수 있습니다.


1

나는 이전 답변 (삭제)에서 성급했지만 실제 답변은 있습니다. 그것은 유사한 개념, factoradic에 의해 제공되며 순열과 관련이 있습니다 (조합과 관련된 내 대답, 그 혼란에 대해 사과드립니다). 나는 위키피디아 링크를 게시하는 것이 싫지만 얼마 전에 내가 쓴 글은 어떤 이유에서인지 이해할 수 없습니다. 따라서 요청시 나중에 확장 할 수 있습니다.


1

이것에 대해 쓰여진 책이 있습니다. 죄송합니다. 이름이 기억 나지 않습니다 (위키 백과에서 찾을 수있을 것입니다). 그러나 어쨌든 나는 그 열거 시스템의 파이썬 구현을 썼습니다 : http://kks.cabal.fi/Kombinaattori 일부는 핀란드어로되어 있지만 코드와 이름 변수를 복사하십시오 ...


0

이 정확한 질문이 있었고 Python 솔루션을 제공 할 것이라고 생각했습니다. O (n ^ 2)입니다.

import copy

def permute(string, num):
    ''' generates a permutation '''
    def build_s(factoradic): # Build string from factoradic in list form
        string0 = copy.copy(string)
        n = []
        for i in range(len(factoradic)):
            n.append(string0[factoradic[i]])
            del string0[factoradic[i]]
        return n

    f = len(string)
    factoradic = []
    while(f != 0): # Generate factoradic number list
        factoradic.append(num % f)
        num = (num - factoradic[-1])//f
        f -= 1

    return build_s(factoradic)

s = set()
# Print 120 permutations of this string
for i in range(120):
    m = permute(list('abcde'), i)
    s.add(''.join(m))

print(len(s)) # Check that we have 120 unique permutations

매우 간단합니다. 숫자의 factoradic 표현을 생성 한 후 문자열에서 문자를 선택하고 제거합니다. 문자열에서 삭제하는 것이 이것이 O (n ^ 2) 솔루션 인 이유입니다.

Antoine의 솔루션은 성능면에서 더 좋습니다.


-1

관련 질문은 순열 배열 만 알고있을 때 순열 된 벡터를 원래 순서로 복원하는 순열 인 역 순열을 계산하는 것입니다. 다음은 O (n) 코드 (PHP)입니다.

// Compute the inverse of a permutation
function GetInvPerm($Perm)
    {
    $n=count($Perm);
    $InvPerm=[];
    for ($i=0; $i<$n; ++$i)
        $InvPerm[$Perm[$i]]=$i;
    return $InvPerm;
    } // GetInvPerm

David Spector Springtime 소프트웨어

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.