Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

combinations of N same balls in A different boxes

int f(int n,int a,int x)
{
        if(a==1)
        {
            if(n>=0 && n<=x)  //HERE WAS ERROR,sorry
                return 1;
            else 
                return 0;
        }

        int ans=0;

        for(int i=0;i<=x;i++)
            ans += f(n-i,a-1,x);

    return ans;
}

Hello! enter image description here

Example:

enter image description here

Here is algorithm, but it spends very much time. Maybe you know a faster way to solve this problem? Thanks very much and sorry for worry.

like image 729
Lu Vue Avatar asked Nov 10 '11 11:11

Lu Vue


2 Answers

What you need is dynamic programming. You need to memorize values of function f for those arguments for which it already have been calculated. Also it can be implemented without recursion like this:

int f(int n,int a,int x)
{
    int q[1000][50]; // to simplify and not use dynamic allocation assume that a < 50 and n < 1000

    q[0][0] = 1;
    for (int i = 1; i < 1000; ++i)
        q[i][0] = 0;

    for (int i = 1; i <= a; ++i)
    {
        for (int j = 0; j <= n; j++)
        {
            int t = 0;
            for (int l = 0; l <= j && l <= x; ++l)
                t += q[j - l][i-1];
            q[j][i] = t;
        }
    }

    return q[n][a];
}

This is only simple technique demonstration. It can be optimized one more time, you can precalculate t-sum and eliminate loop for l. And you don't have to store whole table q, you only need two layers, it will reduce memory usage. So the result will look like this:

int f(int n,int a,int x)
{
    int q[1000][2]; // to simplify and not use dynamic allocation assume n < 1000

    q[0][0] = 1;
    for (int i = 1; i < 1000; ++i)
        q[i][0] = 0;

    int current = 1;
    for (int i = 1; i <= a; ++i)
    {
        int t = 0;
        for (int j = 0; j <= n; j++)
        {
            t += q[j][1 - current];
            if (j > x)
                t -= q[j - x - 1][1 - current];

            q[j][current] = t;
        }
        current = 1 - current;
    }

    return q[n][1 - current];
}

So finally it will take O(a*n) time to compute.

PS: Note that answer can be a huge number which can overflow any native integer type.

like image 171
Wisdom's Wind Avatar answered Sep 19 '22 19:09

Wisdom's Wind


First of all, if A*X < N, there's no way to distribute the balls, so you can stop earlier. If A*X == N, there's only one way. Then it's probably faster to first pick the number of boxes in which you place X balls and recur with a smaller limit.

int f(int n, int a, int x){   // should all be unsigned, actually
    if (n == 0){
        return 1;
    }
    int p = a*x;
    if (p < n){
        return 0;
    }
    if (p == n){
        return 1;
    }
    if (x == 1){
        return binom(a,n);    // ways to choose n boxes from a boxes
    }
    // now the interesting cases
    int ways = 0;    // should perhaps be unsigned long long, that number grows fast
    int xCount, tempRes, min, max;
    min = a+n-p;
    if (min < 0) min = 0;
    max = n/x;
    for(xCount = min; xCount <= max; ++xCount){
        tempRes = f(n - x*xCount,a - xCount, x-1); // ways to distribute the remaining balls
        ways += binom(a,xCount)*tempRes;    // multiply by the number of ways to choose xCount boxes
    }
    return ways;
}

It might be useful to create a table for the binomial coefficients if you call f often.

like image 28
Daniel Fischer Avatar answered Sep 21 '22 19:09

Daniel Fischer