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

时间:2018-08-19 18:37:02

标签: algorithm math combinations combinatorics sequences

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

004013022031040103112121130202211220301310400

第一个排列( N = 0 )是004即存储桶1 = 0球存储桶2 = 0球存储桶3 = 4个球),最后一个( 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
  

PS:该序列的最大术语数是(Bl + Bk-1)C(Bk-1),其中 C 是组合/组合运算符。 Obtained from stars and bars

2 个答案:

答案 0 :(得分:2)

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

我们只需计算所选索引进入每个存储桶的球数,一次工作一个存储桶即可。对于铲斗的每个可能分配,我们计算剩余的球和铲斗的可能布置数量。如果索引小于该数字,则选择该排列;如果索引小于该数字,则选择该排列。否则,我们会再增加一个球,然后从索引中减去刚跳过的安排数。

这是一个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;
}

答案 1 :(得分:1)

可以做出一些有趣的双射。最后,我们可以对常规k组合使用排名和取消排名方法,这是更常见的知识。

  1. 从每个铲斗的球数到铲斗选择的有序多重集的双射;例如:php artisan storage:link(1的三个选择和2的一个选择)。

  2. 通过将[3, 1, 0] --> [1, 1, 1, 2]映射到{1...n},从{1...n + k − 1}的k个子集(有重复)到{c_0, c_1...c_(k−1)}的k个子集(无重复)的双射(请参阅here)。

这是一些python代码:

{c_0, c_(1+1), c_(2+2)...c_(k−1+k−1)}

输出:

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)