Algorithm 求给定排列的索引

Algorithm 求给定排列的索引,algorithm,permutation,space,combinatorics,Algorithm,Permutation,Space,Combinatorics,我正在以某种顺序一个接一个地读取数字0,1,…(N-1)。我的目标是只使用O(1)空间,找到这个给定排列的词典索引 以前有人问过这个问题,但我能找到的所有算法都使用了O(N)space。我开始认为这是不可能的。但这对减少分配数量确实有很大帮助。考虑到以下数据: chars = [a, b, c, d] perm = [c, d, a, b] ids = get_indexes(perm, chars) = [2, 3, 0, 1] 重复排列的可能解决方案如下所示: len = length(p

我正在以某种顺序一个接一个地读取数字
0,1,…(N-1)
。我的目标是只使用
O(1)
空间,找到这个给定排列的词典索引


以前有人问过这个问题,但我能找到的所有算法都使用了
O(N)
space。我开始认为这是不可能的。但这对减少分配数量确实有很大帮助。

考虑到以下数据:

chars = [a, b, c, d]
perm = [c, d, a, b]
ids = get_indexes(perm, chars) = [2, 3, 0, 1]
重复排列的可能解决方案如下所示:

len = length(perm)         (len = 4)
num_chars = length(chars)  (len = 4)

base = num_chars ^ len     (base = 4 ^ 4 = 256)
base = base / len          (base = 256 / 4 = 64)

id = base * ids[0]         (id = 64 * 2 = 128)
base = base / len          (base = 64 / 4 = 16)

id = id + (base * ids[1])  (id = 128 + (16 * 3) = 176)
base = base / len          (base = 16 / 4 = 4)

id = id + (base * ids[2])  (id = 176 + (4 * 0) = 176)
base = base / len          (base = 4 / 4 = 1)

id = id + (base * ids[3])  (id = 176 + (1 * 1) = 177)
反向过程:

id = 177
(id / (4 ^ 3)) % 4 = (177 / 64) % 4 =   2 % 4 = 2 -> chars[2] -> c
(id / (4 ^ 2)) % 4 = (177 / 16) % 4 =  11 % 4 = 3 -> chars[3] -> d
(id / (4 ^ 1)) % 4 = (177 / 4)  % 4 =  44 % 4 = 0 -> chars[0] -> a
(id / (4 ^ 0)) % 4 = (177 / 1)  % 4 = 177 % 4 = 1 -> chars[1] -> b
可能的排列数由
num\u chars^num\u perm\u digits
给出,其中
num\u chars
作为可能的字符数,而
num\u perm\u digits
作为排列中的位数

考虑到初始列表是一个固定成本,这需要在空间中使用
O(1)
;它需要时间
O(N)
,将
N
作为排列的位数

根据上述步骤,您可以执行以下操作:

function identify_permutation(perm, chars) {

    for (i = 0; i < length(perm); i++) {
        ids[i] = get_index(perm[i], chars);
    }

    len = length(perm);
    num_chars = length(chars);

    index = 0;
    base = num_chars ^ len - 1;
    base = base / len;
    for (i = 0; i < length(perm); i++) {
        index += base * ids[i];
        base = base / len;
    }

}
函数标识\u置换(置换、字符){
对于(i=0;i

这是一个伪代码,但也很容易转换成任何语言(:

有N!个置换。要表示索引,至少需要N位。

如果您想假设算术运算是常数时间,这里有一种方法:

def permutationIndex(numbers):
  n=len(numbers)
  result=0
  j=0
  while j<n:
    # Determine factor, which is the number of possible permutations of
    # the remaining digits.
    i=1
    factor=1
    while i<n-j:
      factor*=i
      i+=1
    i=0
    # Determine index, which is how many previous digits there were at
    # the current position.
    index=numbers[j]
    while i<j:
      # Only the digits that weren't used so far are valid choices, so
      # the index gets reduced if the number at the current position
      # is greater than one of the previous digits.
      if numbers[i]<numbers[j]:
        index-=1
      i+=1
    # Update the result.
    result+=index*factor
    j+=1
  return result
def排列索引(数字):
n=len(数字)
结果=0
j=0

j如果你正在寻找一种方法来获得唯一组合的字典索引或排名,而不是排列,那么你的问题就属于二项式系数。二项式系数处理的问题是,在总共N个项目的K组中选择唯一组合

我用C#编写了一个类来处理处理处理二项式系数的常用函数。它执行以下任务:

  • 将任意N选择K的所有K索引以良好的格式输出到文件中。K索引可以替换为更多描述性字符串或字母

  • 将K-索引转换为已排序的二项式系数表中条目的适当词典索引或秩。此技术比依赖迭代的较旧已发布技术快得多。它通过使用Pascal三角形固有的数学特性来实现这一点,与在集合上迭代相比非常有效

  • 将排序后的二项式系数表中的索引转换为相应的K索引。我相信它也比旧的迭代解决方案快

  • 使用此方法计算二项式系数,它不太可能溢出,并且适用于较大的数字

  • 该类是用.NET C#编写的,提供了一种管理与问题相关的对象(如果有)的方法通过使用泛型列表。该类的构造函数接受一个名为InitTable的bool值,如果为true,将创建一个泛型列表来保存要管理的对象。如果该值为false,则它将不会创建该表。使用上述4种方法不需要创建该表。提供了访问器方法来访问e桌

  • 有一个相关的测试类,它展示了如何使用这个类及其方法。它已经用2个案例进行了广泛的测试,并且没有已知的bug

  • 要阅读有关此类的信息并下载代码,请参阅

    以下测试代码将遍历每个唯一的组合:

    public void Test10Choose5()
    {
       String S;
       int Loop;
       int N = 10;  // Total number of elements in the set.
       int K = 5;  // Total number of elements in each group.
       // Create the bin coeff object required to get all
       // the combos for this N choose K combination.
       BinCoeff<int> BC = new BinCoeff<int>(N, K, false);
       int NumCombos = BinCoeff<int>.GetBinCoeff(N, K);
       // The Kindexes array specifies the indexes for a lexigraphic element.
       int[] KIndexes = new int[K];
       StringBuilder SB = new StringBuilder();
       // Loop thru all the combinations for this N choose K case.
       for (int Combo = 0; Combo < NumCombos; Combo++)
       {
          // Get the k-indexes for this combination.  
          BC.GetKIndexes(Combo, KIndexes);
          // Verify that the Kindexes returned can be used to retrive the
          // rank or lexigraphic order of the KIndexes in the table.
          int Val = BC.GetIndex(true, KIndexes);
          if (Val != Combo)
          {
             S = "Val of " + Val.ToString() + " != Combo Value of " + Combo.ToString();
             Console.WriteLine(S);
          }
          SB.Remove(0, SB.Length);
          for (Loop = 0; Loop < K; Loop++)
          {
             SB.Append(KIndexes[Loop].ToString());
             if (Loop < K - 1)
                SB.Append(" ");
          }
          S = "KIndexes = " + SB.ToString();
          Console.WriteLine(S);
       }
    }
    
    public void Test10Choose5()
    {
    字符串S;
    内环;
    int N=10;//集合中的元素总数。
    int K=5;//每组中的元素总数。
    //创建获取所有数据所需的bin coeff对象
    //此N选择K组合的组合。
    BinCoeff BC=新的BinCoeff(N,K,false);
    int NumCombos=BinCoeff.GetBinCoeff(N,K);
    //Kindexes数组指定lexigraphic元素的索引。
    int[]KIndexes=新的int[K];
    StringBuilder SB=新的StringBuilder();
    //循环通过此N选择K案例的所有组合。
    for(int-Combo=0;Combo

    您应该能够相当容易地将此类移植到您选择的语言。您可能不必移植到类的通用部分来实现您的目标。根据您使用的组合数量,您可能需要使用比4字节整数更大的字长。

    有一个java解决方案可以解决此问题geekviewpoint上的问题。它很好地解释了为什么它是真的,代码也很容易理解。它还有一个单元测试,可以用不同的输入运行代码。

    这个想法没有什么新的,只是一个完全矩阵化的方法,没有显式的循环或递归(使用Numpy,但很容易适应):


    我刚刚用VisualBasic写了一个代码,我的程序可以直接计算每个索引或每个corresp
    import numpy as np
    import math
    vfact = np.vectorize(math.factorial, otypes='O')
    
    def perm_index(p):
        return np.dot( vfact(range(len(p)-1, -1, -1)),
                       p-np.sum(np.triu(p>np.vstack(p)), axis=0) )