Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

how do I get the Nth arrangement out of all possible combinations of arranging 4 indistinguishable balls in 3 distinct buckets. if Bl = number of balls and Bk = number of buckets e.g. for Bl = 4, Bk = 3 the possible arrangements are :

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

the first arrangement(N=0) is 004(i.e. bucket 1 = 0 balls, bucket 2 = 0 balls, bucket 3 = 4 balls) and the last(N=14) is 400. so say I have 103 N would be equal to 5. I want to be able to do

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

like image 259
LiNKeR Avatar asked Oct 16 '22 14:10

LiNKeR


2 Answers

As far as I know, there is no faster way of doing this than combinatorial decomposition which takes roughly O(Bl) time.

We simply compute the number of balls which go into the each bucket for the selected index, working one bucket at a time. For each possible assignment to the bucket we compute the number of possible arrangements of the remaining balls and buckets. If the index is less than that number, we select that arrangement; otherwise we put one more ball in the bucket and subtract the number of arrangements we just skipped from the index.

Here's a C implementation. I didn't include the binom function in the implementation below. It's usually best to precompute the binomial coefficients over the range of values you are interested in, since there won't normally be too many. It is easy to do the computation incrementally but it requires a multiplication and a division at each step; while that doesn't affect the asymptotic complexity, it makes the inner loop much slower (because of the divide) and increases the risk of overflow (because of the multiply).

/* 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;
}
like image 91
rici Avatar answered Oct 20 '22 13:10

rici


There are some interesting bijections that can be made. Finally, we can use ranking and unranking methods for the regular k-combinations, which are more common knowledge.

  1. A bijection from the number of balls in each bucket to the ordered multiset of choices of buckets; for example: [3, 1, 0] --> [1, 1, 1, 2] (three choices of 1 and one choice of 2).

  2. A bijection from the k-subsets of {1...n} (with repetition) to k-subsets of {1...n + k − 1} (without repetition) by mapping {c_0, c_1...c_(k−1)} to {c_0, c_(1+1), c_(2+2)...c_(k−1+k−1)} (see here).

Here's some python code:

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)

Output:

"""
[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]
"""
like image 38
גלעד ברקן Avatar answered Oct 20 '22 14:10

גלעד ברקן