代码之家  ›  专栏  ›  技术社区  ›  ijw

快速排列->数字->排列映射算法

  •  101
  • ijw  · 技术社区  · 15 年前

    我有n个元素。例如,7个元素1234567。我知道有7个!=5040这7种元素可能的排列。

    我想要一个包含两个函数的快速算法:

    f(数字)将0到5039之间的数字映射为唯一排列,并且

    f'(排列)将排列映射回从中生成的编号。

    我不关心数字和排列之间的对应关系,只要每个排列都有自己的唯一数字。

    例如,我可能有一些函数

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

    想到的最快的算法是枚举所有排列并在两个方向创建一个查找表,这样,一旦创建了表,f(0)将是o(1),f(“1234567”)将是字符串上的查找。然而,这是一个内存不足的问题,尤其是当n变大时。

    有人能提出另一种算法吗?这种算法既能快速工作,又不会对内存造成不利影响。

    11 回复  |  直到 6 年前
        1
  •  149
  •   Joren    14 年前

    要描述n个元素的排列,您可以看到,对于第一个元素结束的位置,您有n个可能性,所以您可以用一个介于0和n-1之间的数字来描述它。对于下一个元素结束的位置,您还有n-1个可能,所以您可以用一个介于0和n-2之间的数字来描述它。
    等等,直到你有N个数字。

    以n=5为例,考虑 abcde caebd .

    • a ,第一个元素,在第二个位置结束,所以我们为它分配索引 1个 .
    • b 最后到达第四个位置,也就是索引3,但它是剩下的第三个,所以我们将其分配给 .
    • c 在第一个剩余位置结束,这是 .
    • d 最后一个剩余位置结束,其中(只有两个剩余位置)是 .
    • e 在唯一剩余的位置结束,索引在 .

    所以我们有了索引序列 { 1, 2, 0,1, 0 } .

    现在您知道了,例如在二进制数中,“xyz”表示z+2y+4x。对于十进制数,
    是z+10y+100x,每个数字乘以某个权重,结果相加。当然,权重中的明显模式是,权重是w=b^k,其中b是数字的基数,k是数字的索引。(我总是从右边开始计算数字,从索引0开始计算最右边的数字。同样,当我谈论第一个数字时,我指的是最右边的数字。)

    这个 原因 为什么数字的权重遵循这种模式,是指可以用从0到k的数字表示的最高数字必须正好比只能用数字k+1表示的最低数字低1。在二进制中,0111必须小于1000。十进制中,099999必须小于100000。

    编码到变量基
    后面的数字之间的间距正好是1,这是一个重要的规则。意识到这一点,我们可以用 可变基数 . 每个数字的基数是该数字不同可能性的数量。对于十进制,每个数字有10种可能,对于我们的系统,最右边的数字有1种可能,最左边的数字有N种可能。但是,由于最右边的数字(我们序列中的最后一个数字)总是0,所以我们不考虑它。这意味着我们剩下的基数是2到n。一般来说,K位数的基数是b[k]=k+2。数字k允许的最大值是h[k]=b[k]-1=k+1。

    我们关于数字权重w[k]的规则要求h[i]*w[i]的和(其中i从i=0到i=k)等于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'th(最右边,从0开始)元素。以我们的1、2、0、1、0为例,去掉最右边的元素,如前所述: { 1, 2, 0,1 } . 我们的总数是1*1+0*2+2*6+1*24= 三十七 .

    注意,如果我们取每个索引的最大位置,我们会得到4、3、2、1、0,然后转换为119。因为我们选择了数字编码中的权重,这样我们就不会跳过任何数字,所以所有数字0到119都是有效的。其中正好有120个,就是n!对于我们的例子中的n=5,精确地表示不同排列的数目。所以您可以看到我们的编码数字完全指定了所有可能的排列。

    从可变基解码
    解码类似于转换为二进制或十进制。常见的算法是:

    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
    }
    

    这正确地将我们的37解码回1、2、0、1( sequence 将是 {1, 0, 2, 1} 在这个代码示例中,但是无论如何…只要你的索引合适)。我们只需要在右端添加0(记住最后一个元素的新位置总是只有一个可能性),就可以恢复我们原来的序列1、2、0、1、0。

    使用索引序列排列列表
    您可以使用下面的算法根据特定的索引序列排列列表。不幸的是,这是一个O(n_2)算法。

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

    从我们的表象到共同表象的转换
    注意,如果我们使用我们的索引序列对一个列表进行排序,并将其应用于标识排列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_2)。如果需要多次应用置换,请首先将其转换为公共表示。

        2
  •  14
  •   Antoine Comeau    9 年前

    我在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;
    }
    
        3
  •  7
  •   user416260    14 年前

    复杂性可降至n*log(n),见第10.1.1节。 fxtbook的(“Lehmer代码(倒置表)”,第232ff页): http://www.jjj.de/fxt/#fxtbook 对于快速方法,请跳到第10.1.1.1节(“大数组计算”P.235)。 (GPUL,C++)代码在同一个网页上。

        4
  •  4
  •   sbi    15 年前

    每个元素可以位于七个位置中的一个。要描述一个元素的位置,需要三位。这意味着您可以以32位值存储所有元素的位置。这远不是有效的,因为这种表示甚至允许所有元素处于相同的位置,但我相信位屏蔽应该相当快。

    但是,有8个以上的职位,你需要更漂亮的东西。

        5
  •  4
  •   ephemient    15 年前

    这恰好是内置函数 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
    
        6
  •  4
  •   Fred Pang    9 年前

    问题解决了。但是,我不确定这些年后你是否仍然需要这个解决方案。哈哈,我刚加入这个网站,所以… 检查我的Java置换类。您可以基于索引获取符号排列,或者给出符号排列,然后获取索引。

    这是我的预备班

    /**
     ****************************************************************************************************************
     * 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
  •  2
  •   Keith Randall    15 年前

    可以使用递归算法对排列进行编码。如果n-排列(数字0,..,n-1的某种顺序)是x,…的形式,那么将其编码为x+n*在数字0,n-1-x上用“…”表示的(n-1)-排列的编码。听起来像一口,下面是一些代码:

    // 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)算法,则可获得奖励积分。

        8
  •  1
  •   kyokley    15 年前

    多么有趣的问题!

    如果所有元素都是数字,那么您可能需要考虑将它们从字符串转换为实际数字。然后,您将能够通过将排列排列顺序排列来对所有排列进行排序,并将它们放置在一个数组中。在那之后,您将可以使用任何一种不同的搜索算法。

        9
  •  1
  •   nlucaroni    15 年前

    我在先前的回答(删除)中很仓促,但我确实有实际的答案。它由类似的概念提供,即 factoradic 和排列有关(我的答案与组合有关,我为这种混淆道歉)。我不喜欢仅仅发布维基百科链接,但我写了一段时间前我做的事情,因为某些原因是不明白的。因此,如果有要求,我可以稍后对此进行扩展。

        10
  •  1
  •   kummahiih    14 年前

    有一本关于这个的书。对不起,我不记得它的名字了(你很可能会在维基百科上找到它)。 但无论如何,我写了一个枚举系统的python实现: http://kks.cabal.fi/Kombinaattori 其中一些是芬兰语的,但只需复制代码和名称变量…

        11
  •  0
  •   David Spector    6 年前

    一个相关的问题是计算逆排列,当只知道排列数组时,它将排列向量恢复到原来的顺序。以下是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
    

    斯佩克特 Springtime软件