如何获得组合序列中的第 N 个排列,反之亦然?

How to get the Nth arrangement in a Combinatoric sequence and vice-versa?

如何从将 4 个无法区分的球排列在 3 个不同的桶中的所有可能组合中得到 Nth 排列。如果 Bl = number of ballsBk = number of buckets 例如对于 Bl = 4, Bk = 3 可能的排列是:

004,013,022,031,040,103,112,121,130,202,211,220,301,310,400 .

第一个排列(N=0)是004(即bucket 1 = 0 balls,bucket 2 = 0 balls, bucket 3 = 4 balls) 最后一个(N=14)是400。所以说我有 103 N 将等于 5。我希望能够做到

int Bl=4,Bk=3;
getN(004,Bl,Bk);// which should be = 0
getNthTerm(8,Bl,Bk);// which should be = 130

P.S: max number of terms for the sequence is (Bl+Bk-1)C(Bk-1) where C is the combinatorics/combination operator. Obtained from stars and bars

据我所知,没有比组合分解更快的方法了,它大约需要 O(Bl) 时间。

我们简单地计算进入 selected 索引的每个桶的球数,一次处理一个桶。对于桶的每个可能分配,我们计算剩余球和桶的可能排列数。如果索引小于那个数,我们select那个排列;否则我们再往桶里放一个球,然后从索引中减去我们刚刚跳过的排列数。

这是一个 C 实现。我没有在下面的实现中包含 binom 函数。通常最好预先计算您感兴趣的值范围内的二项式系数,因为通常不会太多。增量计算很容易,但每一步都需要乘法和除法;虽然这不会影响渐近复杂性,但它会使内部循环变慢(因为除法)并增加溢出的风险(因为乘法)。

/* Computes arrangement corresponding to index.
 * Returns 0 if index is out of range.
 */
int get_nth(long index, int buckets, int balls, int result[buckets]) {
  int i = 0;
  memset(result, 0, buckets * sizeof *result);
  --buckets;
  while (balls && buckets) {
    long count = binom(buckets + balls - 1, buckets - 1);
    if (index < count) { --buckets; ++i; }
    else { ++result[i]; --balls; index -= count; }
  }
  if (balls) result[i] = balls;
  return index == 0;
}

可以进行一些有趣的双射。最后,对于正则的k-组合,我们可以使用ranking和unranking的方法,这是比较常识。

  1. 从每个桶中的球数到桶选择的有序多重集的双射;例如:[3, 1, 0] --> [1, 1, 1, 2](三选1,一选2)。

  2. 通过将 {c_0, c_1...c_(k−1)} 映射到 [,从 {1...n} 的 k 个子集(有重复)到 {1...n + k − 1} 的 k 个子集(没有重复)的双射=16=](参见 here)。

这是一些 python 代码:

from itertools import combinations_with_replacement

def toTokens(C):
  return map(lambda x: int(x), list(C))

def compositionToChoice(tokens):
  result = []
  for i, t in enumerate(tokens):
    result = result + [i + 1] * t
  return result

def bijection(C):
  result = []
  k = 0
  for i, _c in enumerate(C):
    result.append(C[i] + k)
    k = k + 1
  return result

compositions = ['004','013','022','031','040','103','112',
                '121','130','202','211','220','301','310','400']

for c in compositions:
  tokens = toTokens(c)
  choices = compositionToChoice(tokens)
  combination = bijection(choices)
  print "%s  -->  %s  -->  %s" % (tokens, choices, combination)

输出:

"""
[0, 0, 4]  -->  [3, 3, 3, 3]  -->  [3, 4, 5, 6]
[0, 1, 3]  -->  [2, 3, 3, 3]  -->  [2, 4, 5, 6]
[0, 2, 2]  -->  [2, 2, 3, 3]  -->  [2, 3, 5, 6]
[0, 3, 1]  -->  [2, 2, 2, 3]  -->  [2, 3, 4, 6]
[0, 4, 0]  -->  [2, 2, 2, 2]  -->  [2, 3, 4, 5]
[1, 0, 3]  -->  [1, 3, 3, 3]  -->  [1, 4, 5, 6]
[1, 1, 2]  -->  [1, 2, 3, 3]  -->  [1, 3, 5, 6]
[1, 2, 1]  -->  [1, 2, 2, 3]  -->  [1, 3, 4, 6]
[1, 3, 0]  -->  [1, 2, 2, 2]  -->  [1, 3, 4, 5]
[2, 0, 2]  -->  [1, 1, 3, 3]  -->  [1, 2, 5, 6]
[2, 1, 1]  -->  [1, 1, 2, 3]  -->  [1, 2, 4, 6]
[2, 2, 0]  -->  [1, 1, 2, 2]  -->  [1, 2, 4, 5]
[3, 0, 1]  -->  [1, 1, 1, 3]  -->  [1, 2, 3, 6]
[3, 1, 0]  -->  [1, 1, 1, 2]  -->  [1, 2, 3, 5]
[4, 0, 0]  -->  [1, 1, 1, 1]  -->  [1, 2, 3, 4]
"""