1

I'm looking to figure out the formula for putting K balls in N urns, where each of the N urns has a specific max capacity. For example,

  • Urn A has capacity for at most 5 balls
  • Urn B has capacity for at most 2 balls
  • Urn C has capacity for at most 1 ball
  • Urn D has capacity for at most 2 balls

How many ways are there to put 5 indistinguishable balls in urns A, B, C and D?

With brute-force code, I know that this example has these 18 solutions:

(0, 2, 1, 2) (1, 1, 1, 2) (1, 2, 0, 2) (1, 2, 1, 1) (2, 0, 1, 2) (2, 1, 0, 2) (2, 1, 1, 1) (2, 2, 0, 1) (2, 2, 1, 0) (3, 0, 0, 2) (3, 0, 1, 1) (3, 1, 0, 1) (3, 1, 1, 0) (3, 2, 0, 0) (4, 0, 0, 1) (4, 0, 1, 0) (4, 1, 0, 0) (5, 0, 0, 0)

But I'm having trouble coming up with a general formula, which I need for my application, where the numbers get too large to brute-force like this. Does anyone know how to compute the number of combinations for putting indistinguishable balls in urns, only with max capacity for each urn?

  • 2
    You can use generating function or stars and bars along with principle of inclusion exclusion. But for bigger numbers, generating function would scale better (I am assuming you are writing a program) – Math Lover Oct 19 '21 at 02:47
  • @MathLover I have the code to create a generating function, but I really just need the number of combinations, not each combination; that's what I'm looking for here. I'm aiming to avoid iterating through all the many combinations.

    What do you mean by stars and bars with the principle of inclusion exclusion?

    – MooseOfJoy Oct 19 '21 at 02:49
  • 2
    For generating function, if an urn can have max of $1$ ball, write it as $(x^0+x^1)$ or $(1+x)$, if it can have at most $2$ balls write it as $(1+x+x^2)$ and so on... So it comes down to finding coefficient of $x^5$ in $(1+x) (1+x+x^2)^2 (1 + x + x^2 + x^3 + x^4 + x^5)$ – Math Lover Oct 19 '21 at 03:07
  • @MathLover Oh, interesting! I was thinking of a generator, I'd forgotten about this usage of generating function. Would the same function work for different sets of maxima? I have many thousands of maxima – MooseOfJoy Oct 19 '21 at 03:12
  • Wild guess: $\frac{m!}{n_1!n_2!...n_N!}$ where $n_k\le N_k$, and $n_k $ is no. balls with $N_k$ is capacity of urn $k$ among $N$ and $\sum n_k=m$ is number of balls. – herb steinberg Oct 19 '21 at 04:01
  • @herbsteinberg That looks like what I'm looking for! But I'm not sure I follow the exact formula — can you walk through how you would use it on the example & recover the value 18? – MooseOfJoy Oct 19 '21 at 04:12
  • As stated it is incorrect. It needs a summation over combinations of $n_k$. It may turn out no better than brute force. – herb steinberg Oct 19 '21 at 17:09

2 Answers2

1

After researching, and in particular looking at the answers to this post Number of ways to distribute indistinguishable balls into distinguishable boxes of given size and this post Using generating functions in combinatorics, I realize that @MathLover's comment above is right — thank you! Quoted here:

For generating function, if an urn can have max of 1 ball, write it as $(_0+_1)$ or $(1+)$, if it can have at most 2 balls write it as $(1++_2)$ and so on... So it comes down to finding coefficient of $_5$ in $(1+)(1++_2)^2(1++_2+_3+_4+_5)$

For those less interested in the pure formula than in the implementation, as I am, here's the code I'm now using:

import numpy as np

def comb_r_under_max(r,max_by_urn): mul = np.poly1d(np.ones(max_by_urn[0]+1)) # initializing polynomial for deg in max_by_urn[1:]: mul = np.polymul(np.poly1d(np.ones(deg+1)), mul) # multiplying return int(mul[r])

test on our example

max_by_urn = [5,2,1,2] r = 5

print(comb_r_under_max(r,max_by_urn)) # prints 18

0

one idea is to solve this kind of problems with recusion and lru_cache, from ground up, code can be simplified but I am sure you can work it out

@lru_cache
def get_combs(no_balls: int, urns: tuple) -> int:
    if no_balls == 0:
        return 1
    if no_balls > sum(urns):
        return 0
    if len(urns) ==  1:
        if urns[0]>=no_balls:
            return 1
        else:
            return 0
    res = 0
    for i in range(min(urns[0], no_balls)+1):
        res += get_combs(no_balls-i, urns[1:])
    return res
quester
  • 607