开发者

Recursive backtracking

开发者 https://www.devze.com 2023-02-13 13:52 出处:网络
I am having a problem with my backtracking function it loops with certain data I can\'t write here the whole program code but can put here my function.

I am having a problem with my backtracking function it loops with certain data I can't write here the whole program code but can put here my function.

bool shareMoney(int quantity, int *values, int index, int moneyA, int half, bool *ifChosen)
{

    if(moneyA == half)
        return true;
    else if(index >= quantity)
        return false;

    moneyA += values[index];

    if(shareMoney(quantity, values,index+1, moneyA, half, ifChosen))
    {
        ifChosen[index] = true;
        return true;
    };

    moneyA -= values[index];

    if(shareMoney(quantity, values,index+1, moneyA, half, ifChosen))
    {
        ifChosen[index] = false;
        return true;
    };

    return false;
}

Now here is the explanation:

quantity - number of elements in values array

values - array of numbers

index - variable to control the recursion

moneyA - variable that stores the sum of element from array values

half - number that moneyA should reach after recursion is done

ifChosen - array of boolean elements that refers to array values

The function gets quantity of elements which is lenght of values array, values an array with numbers in it's sorted from the highest to the lowest one, index controls recursion and default it's 0 so it starts from the first element, moneyA variable that stores numbers from the values array and it should reach half which is the half of numbers sumed from values array and ifChosen stores numbers that are chosen.

The whole function does this, it sums the elements from the values array and checks wether it reache开发者_如何学编程d the half if it's lower than half adds it to moneyA and mark it in ifChosen then it goes to next one, if the sum is higher than half it gets back and unmark it in ifChosen array and substract from moneyA. It should always get the highest elements.

Here is the simple example:

6 - number of elements
50, 20, 19, 15, 2, 2 - elements stored in values array
total sum is - 108
half of elements - 54

The result for this one should be:

50, 2, 2 - marked as true in ifChosen
20, 19, 15 - marked as false in ifChosen 

Of course for this simple example it does great job but for more complicated that have more numbers and one number occurs more than once it loops and recursion never stops. I've been actually working on this for 1.5 weeks and asked all my friends but nobody knows what is wrong with it. I know it has a bit to do with knapsack problem but I didn't have that one yet and still have to study.

I'm looking forward to any answer that could help.

I'm sorry for my punctuation but I'm here for the first time and didn't got used to formatting.

Here you got one example:

89 86 83 67 53 45 5 1    

44 43 34 33 33 24 23 23 23 22 21 21 19 12 11 9 8 7 5 3 2 2 2 1 1 1 1 1     

real    0m28.007s    

user    0m27.926s    

sys 0m0.028s

Now the one I think it loops forever: 43 elements:

12 2 2 1 3 4 5 6 7 89 33 12 45 23 44 23 11 44 1 3 5 7 88 33 8 19 43 22 86 5 34 23 21 67 83 24 21 53 9 11 34 1 1

@Karl Bielefeldt Yes I know that there are so many combinations that's why I am trying to speed it up. For now this is all I got but it gives me wrong results for certain input. Can anyone make that correct, it works much faster than before ?

bool shareMoney(int quantity, int *values, int index, int moneyA, int half, bool *ifChosen){

if(index>=quantity && moneyA == half){ return true;}
else if(index>=quantity) return false;

moneyA+=values[index];
ifChosen[index]=true;

if(moneyA<=half){       
    shareMoney(quantity,values,index+1,moneyA, half,ifChosen);
    if(moneyA==half) return true;

    return true;
}else{
    shareMoney(quantity,values,index+1,moneyA, half,ifChosen);      
    moneyA-=values[index];
    ifChosen[index]=false;

    return true;
}


return false;}


The typical way to cut down on the number of iterations on a problem like this is to calculate a bound on a subtree by solving a linear program (like your problem, but the remaining variables are allowed to take on fractional values). Simplex solves the linear program in approximately quadratic time instead of exponential. The best solution for the linear program is at least as good as the best integer or binary solution with the same constraints, so if the linear solution is worse that your current best, you can throw away the whole subtree without exhaustive evaluation.

EDIT: Let's start by simplifying the brute force algorithm:

int* shareMoney( int pool_size, int *pool, int *solution, int cumsum, int goal)
{
    if (cumsum == goal) return solution;
#if PRUNE_ABOVE
    if (cumsum > goal) return 0;
#endif
    if (!pool_size) return 0;

#if PRUNE_BELOW
    int max = cumsum;
    for( int n = pool_size; n--; max += pool[n] );
    if (max < goal) return 0;
#endif

    int* subproblem = shareMoney(pool_size-1, pool+1, solution, cumsum, goal);
    if (subproblem) return subproblem;

    *solution = *pool;
    return shareMoney(pool_size-1, pool+1, solution+1, cumsum+*pool, goal);
}

After execution, solution contains a list of the values used to reach the goal, and the returned pointer indicates the end of the list.

The conditional blocks are my first suggested improvement. No recursion is necessary in these cases.

We can eliminate the need to iterate to calculate max at each step:

int* shareMoney( int pool_size, int *pool, int *solution, int cumsum, int poolsum, int goal)
{
    if (cumsum == goal) return solution;
#if PRUNE_ABOVE
    if (cumsum > goal) return 0;
#endif
    if (!pool_size) return 0;

#if PRUNE_BELOW
    if (cumsum + poolsum < goal) return 0;
#endif

    int* subproblem = shareMoney(pool_size-1, pool+1, solution, cumsum, poolsum - *pool, goal);
    if (subproblem) return subproblem;

    *solution = *pool;
    return shareMoney(pool_size-1, pool+1, solution+1, cumsum+*pool, poolsum - *pool, goal);
}

Here's a function to solve the integer version (better for repeated coin denominations):

int* shareMoney( int pool_size, int *pool_denom, int *pool_cardinality, int *solution, int cumsum, int poolsum, int goal)
{
    if (cumsum == goal) return solution;
#if PRUNE_ABOVE
    if (cumsum > goal) return 0;
#endif
    if (!pool_size) return 0;

#if PRUNE_BELOW
    if (cumsum + poolsum < goal) return 0;
#endif

    poolsum -= *pool_cardinality * *pool_denom;
    for (*solution = *pool_cardinality; *solution >= 0; --*solution) {
        int* subproblem = shareMoney(pool_size-1, pool_denom+1, pool_cardinality+1, solution+1, cumsum + *solution * *pool_denom, poolsum, goal);
        if (subproblem) return subproblem;
    }

    return 0;
}

Instead of getting a straight list of individual coins, it takes a list of denominations, and the number of available coins of each one. The result is the number of coins of each denomination needed by the solution.


For 43 elements, there are close to 9 trillion possible combinations. There's no way to speed that up if you have to check all 9 trillion, but in case you don't want to wait that long, the trick is to try to put the answer closer to the start of the loop. I think you've probably hit on the right solution by sorting it in increasing order. This is probably faster because it gets the big pieces arranged first (because you are doing depth-first recursion).

If I understand the problem correctly, that will find combination of the smallest elements that add up to exactly half the total value. That means the elements that aren't selected also should add up to exactly half the total value, and will be the largest elements.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号