简体   繁体   English

递归回溯

[英]Recursive backtracking

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 quantity - values数组中的元素数
values - array of numbers 值 - 数字数组
index - variable to control the recursion index - 用于控制递归的变量
moneyA - variable that stores the sum of element from array values moneyA - 存储数组值中元素总和的变量
half - number that moneyA should reach after recursion is done 在递归完成后moneyA应该达到的半数
ifChosen - array of boolean elements that refers to array values ifChosen - 引用数组值的布尔元素数组

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. 该函数获取值为数组的元素数量,值为一个数字,其中数字从最高到最低排序,索引控制递归,默认为0,因此它从第一个元素moneyA变量开始,该变量存储来自values数组,它应该达到一半,这是从数值数组中取出的数字的一半,ifChosen存储了所选择的数字。

The whole function does this, it sums the elements from the values array and checks wether it reached 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. 整个函数执行此操作,它将值数组中的元素相加并检查它是否达到了一半,如果它低于一半将其添加到moneyA并标记为ifChosen然后它转到下一个,如果总和高于一半它回来并在ifChosen数组中取消标记并从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. 我已经实际工作了1.5周,并问了我所有的朋友,但没有人知道它有什么问题。 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: 现在,我认为它永远循环:43个元素:

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. @Karl Bielefeldt是的我知道有这么多组合,这就是为什么我要加快速度。 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. Simplex以近似二次时间而非指数方式求解线性程序。 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. 执行后, solution包含用于到达目标的值列表,返回的指针指示列表的结尾。

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. 对于43个元素,有近9万亿种可能的组合。 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. 如果你必须检查所有9万亿,那么就没办法加快速度,但是如果你不想等那么久,诀窍就是试着让答案更接近循环的开始。 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. 这意味着选择的元素应该总计恰好是总值的一半,并且将是最大的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM