【问题标题】:Problem with recursive backtracking递归回溯的问题
【发布时间】:2011-08-09 22:51:18
【问题描述】:

大家好,最近发布了关于我的算法的问题。

Finding the numbers from a set which give the minimum amount of waste

我稍微修改了代码,所以它现在在一定程度上回溯了,但是输出仍然有缺陷。我已经调试了这个相当检查所有变量值,但似乎无法找出问题所在。

再次提出建议而不是直接解决方案会很有帮助。我认为我的代码只有几个问题,但我不知道在哪里。

//来自上一篇:

基本上,一个集合被传递给下面的这个方法,并且一个条的长度也被传递。如果从集合中删除某些数字,则解决方案应该输出集合中的数字,这些数字会产生最少的浪费条长度。所以,条长 10,集合包括 6,1,4,所以解决方案是 6 和 4,浪费是 0。我在通过集合回溯的条件方面遇到了一些问题。我还尝试使用浪费的“全局”变量来帮助回溯方面,但无济于事。

SetInt 是一个手工制作的集合实现,可以添加、删除、检查集合是否为空并返回集合中的最小值。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package recursivebacktracking;

/**
 *
 * @author User
 */
public class RecBack {

        int WASTAGE = 10;
        int BESTWASTAGE;
        int BARLENGTH = 10;

    public void work()
    {


         int[] nums = {6,1,2,5};
        //Order Numbers
        SetInt ORDERS = new SetInt(nums.length);
        SetInt BESTSET = new SetInt(nums.length);
        SetInt SOLUTION = new SetInt(nums.length);

        //Set Declarration


        for (int item : nums)ORDERS.add(item);
        //Populate Set

        SetInt result = tryCutting(ORDERS, SOLUTION, BARLENGTH, WASTAGE);
        result.printNumbers();


    }
    public SetInt tryCutting(SetInt possibleOrders, SetInt solution, int lengthleft, int waste)
      {


        for (int i = 0; i < possibleOrders.numberInSet(); i++) // the repeat
          {


            int a = possibleOrders.min(); //select next candidate
              System.out.println(a);

            if (a <= lengthleft) //if accecptable
              { 
                solution.add(a); //record candidate
                lengthleft -= a;
                WASTAGE = lengthleft;
                possibleOrders.remove(a); //remove from original set



                if (!possibleOrders.isEmpty()) //solution not complete
                  {
                      System.out.println("this time");
                    tryCutting(possibleOrders, solution, lengthleft, waste);//try recursive call
                    BESTWASTAGE = WASTAGE;
                    if ( BESTWASTAGE <= WASTAGE )//if not successfull
                      {
                        lengthleft += a;
                        solution.remove(a);

                          System.out.println("never happens");
                      }

                  } //solution not complete
              }

          } //for loop

        return solution;

      }




}

【问题讨论】:

  • 调试时它总是回到递归调用之后的那一行,我设置 BESTWASTAGE = WASTAGE。这可能会导致问题,如果是这样,最好的设置位置在哪里?

标签: java recursion backtracking


【解决方案1】:

您是否考虑过使用位掩码算法而不是回溯?我认为这会让你的算法更简单。

以下是您将如何执行此操作的概述:

设 N 为集合中的元素数。因此,如果集合是 {6,1,2,5},那么 N 将是 4。让 max_waste 成为我们可以消除的最大浪费(在您的示例中为 10)。

int best = 0;  // the best result so far

for (int mask = 1; mask <= (1<<N)-1; ++mask) {

    // loop over each bit in the mask to see if it's set and add to the sum
    int sm = 0;
    for (int j = 0; j < N; ++j) {
        if ( ((1<<j)&mask) != 0) {
            // the bit is set, add this amount to the total
            sm += your_set[j];

            // possible optimization: if sm is greater than max waste, then break
            // out of loop since there's no need to continue
        }
    }

    // if sm <= max_waste, then see if this result produces a better one
    // that our current best, and store accordingly
    if (sm <= max_waste) {
        best = max(max_waste - sm);
    }
}

此算法与回溯非常相似,复杂度也相似,只是不使用递归。

位掩码基本上是二进制表示,其中 1 表示我们使用集合中的项目,而 0 表示我们不使用。由于我们从 1 循环到 (1&lt;&lt;N)-1,因此我们正在考虑给定项目的所有可能子集。

请注意,随着 N 变大,该算法的运行时间会迅速增加,但 N

对于回溯,您只需要跟踪您所在集合中的哪个元素,然后尝试使用该元素或不使用它。如果你使用它,你把它加到你的总数中,如果没有,你继续下一个递归调用而不增加你的总数。然后,你减少总数(如果你增加了它),这就是回溯的来源。

它与上面的位掩码方法非常相似,我提供了位掩码解决方案,以帮助您更好地了解回溯算法的工作原理。

编辑 好的,我没有意识到你需要使用递归。

提示1 首先,我认为您可以通过使用单个递归函数并将逻辑放入该函数中来大大简化您的代码。无需提前构建所有集合然后处理它们(我不完全确定这就是您正在做的事情,但从您的代码来看似乎是这样)。您可以只构建集合,然后跟踪您在集合中的位置。当你到达这组结束时,看看你的结果是否更好。

提示2 如果您仍然需要更多提示,请尝试考虑回溯功能应该做什么。终止条件是什么?当我们达到终止条件时,我们需要记录什么(例如我们是否得到了新的最佳结果等)?

提示3 剧透警告 下面是一个 C++ 实现,可以为您提供一些想法,如果您想自己进行更多工作,请停止阅读此处。

int bestDiff = 999999999;
int N;
vector< int > cur_items;
int cur_tot = 0;
int items[] = {6,1,2,5};
vector< int > best_items;
int max_waste;

void go(int at) {
  if (cur_tot > max_waste)
    // we've exceeded max_waste, so no need to continue
    return;
  if (at == N) {
    // we're at the end of the input, see if we got a better result and
    // if so, record it
    if (max_waste - cur_tot < bestDiff) {
      bestDiff = max_waste - cur_tot;
      best_items = cur_items;
    }
    return;
  }

  // use this item  
  cur_items.push_back(items[at]);
  cur_tot += items[at];
  go(at+1);

  // here's the backtracking part
  cur_tot -= items[at];
  cur_items.pop_back();

  // don't use this item
  go(at+1);
}

int main() {
  // 4 items in the set, so N is 4
  N=4;

  // maximum waste we can eliminiate is 10
  max_waste = 10;

  // call the backtracking algo
  go(0);

  // output the results
  cout<<"bestDiff = "<<bestDiff<<endl;
  cout<<"The items are:"<<endl;
  for (int i = 0; i < best_items.size(); ++i) {
    cout<<best_items[i]<<" ";
  }
  return 0;
}

【讨论】:

  • 您好,感谢您非常详细的回复。不幸的是,由于这是一项任务,我必须使用递归方法来解决问题。关于你所说的回溯:solution.add(a);我在哪里将它添加到我的解决方案总数中是你的意思吗?还是您在谈论我的 lengthLeft 变量?我试图让回溯与 WASTAGE 和 BESTWASTAGE 变量一起发挥作用,但我对回溯的流程如何为我实现它感到有点困惑。
  • @Newb - 查看我更新的答案,但要谨慎行事,具体取决于您需要多少帮助。祝你好运!
猜你喜欢
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
  • 2020-06-30
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
相关资源
最近更新 更多