递归回溯

发布于 2024-10-19 01:49:26 字数 2634 浏览 2 评论 0原文

我的回溯函数有问题,它会循环某些数据,我无法在这里编写整个程序代码,但可以将我的函数放在这里。

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;
}

现在解释一下:

数量 - 值数组中的元素数量
值 - 数字数组
index - 控制递归的变量
MoneyA - 存储数组值中元素总和的变量
half - 递归完成后 MoneyA 应该达到的数字
ifChosen - 引用数组值的布尔元素数组

该函数获取元素的数量,即值数组的长度,值是一个数组,其中的数字从最高到最低排序,索引控制递归,默认为 0,因此它开始从第一个元素开始,money 是一个变量,用于存储值数组中的数字,它应该达到一半,即从值数组中求和的数字的一半,而 ifChosen 存储所选的数字。

整个函数就是这样做的,它对values数组中的元素进行求和,并检查它是否达到了一半,如果它低于一半,则将其添加到moneyA并在ifChosen中标记它,然后它会转到下一个,如果总和高于它的一半返回并在 ifChosen 数组中取消标记并从 MoneyA 中减去。它应该始终获得最高的元素。

这是一个简单的例子:

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

这个结果应该是:

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

当然,对于这个简单的例子来说,它做得很好,但对于具有更多数字且一个数字出现多次的更复杂的例子,它会循环并且递归永远不会停止。我实际上已经为此工作了 1.5 周,并询问了我所有的朋友,但没有人知道它出了什么问题。我知道这与背包问题有一点关系,但我还没有那个,仍然需要学习。

我期待任何有帮助的答案。

我很抱歉我的标点符号,但我是第一次来这里,不习惯格式。

这里有一个例子:

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

现在我认为它会永远循环: 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 是的,我知道有很多组合,这就是我试图加快速度的原因。现在这就是我得到的全部,但对于某些输入它给了我错误的结果。任何人都可以纠正它,它的工作速度比以前快得多吗?

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;}

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 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. 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;}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

双马尾 2024-10-26 01:49:26

减少此类问题迭代次数的典型方法是通过求解线性程序来计算子树上的界限(就像您的问题一样,但其余变量允许采用小数值)。单纯形法以近似二次方的时间而不是指数的时间求解线性程序。线性程序的最佳解决方案至少与具有相同约束的最佳整数或二进制解决方案一样好,因此,如果线性解决方案比当前最佳解决方案更差,您可以丢弃整个子树而不进行详尽的评估。

编辑:让我们从简化暴力算法开始:

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);
}

执行后,解决方案包含用于达到目标的值的列表,返回的指针指示列表的末尾。

条件块是我建议的第一个改进。在这些情况下不需要递归。

我们可以消除在每一步迭代计算最大值的需要:

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);
}

这是一个解决整数版本的函数(对于重复的硬币面额更好):

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;
}

它不需要获得单个硬币的直接列表,而是需要一个面额列表和数量每个可用硬币的数量。结果是解决方案所需的每种面额的硬币数量。

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.

悲念泪 2024-10-26 01:49:26

对于 43 个元素,有接近 9 万亿种可能的组合。如果您必须检查全部 9 万亿,则没有办法加快速度,但如果您不想等那么久,诀窍是尝试将答案放在更接近循环开始的位置。我认为您可能已经通过按升序对它进行排序找到了正确的解决方案。这可能会更快,因为它首先排列大块(因为您正在进行深度优先递归)。

如果我正确理解这个问题,就会找到最小元素的组合,加起来恰好是总值的一半。这意味着选择的元素加起来应该恰好是总值的一半,并且将是最大的元素。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文