【问题标题】:Optimizing recursive backtrack优化递归回溯
【发布时间】:2019-12-21 00:09:20
【问题描述】:

我通过回溯所有可能的解决方案解决了背包问题的一个变体。基本上 0 表示该物品不在背包中,1 表示该物品在背包中。成本是背包中所有物品的价值,我们试图在拥有每个“类别”物品的同时达到尽可能低的价值。每次找到所有类的组合时,我都会计算所有项目的值,如果它低于 globalBestValue,则保存该值。我这样做是verify()

现在我正在尝试优化我的递归回溯。我的想法是在生成数组时对其进行迭代,如果我生成的数字的“成本”已经高于我当前的最佳值,则返回生成器,因此当前生成的组合不能是新的最佳值并且可以跳过。

但是,通过我的优化,我的回溯并没有生成所有值,它实际上跳过了我试图找到的“最佳”值。你能告诉我问题出在哪里吗?

private int globalBestValue = Integer.MAX_VALUE;
private int[] arr;

public KnapSack(int numberOfItems) {
    arr = new int[numberOfItems];
}

private void generate(int fromIndex) {
    int currentCost = 0; // my optimisation starts here
    for (int i = 0; i < arr.length; i++) {
        if (currentCost > globalBestValue) {
            return;
        }
        if (arr[i] == 1) {
            currentCost += allCosts.get(i);
        }
    } // ends here
    if (fromIndex == arr.length) {
        verify();
        return;
    }
    for (int i = 0; i <= 1; i++) {
        arr[fromIndex] = i;
        generate(fromIndex + 1);
    }
}

public void verify() {
            // skipped the code verifying the arr if it's correct, it's long and not relevant
            if (isCorrect == true && currentValue < globalBestValue) {
                globalBestValue = currentValue;
            }else{
                return;
            }
}

【问题讨论】:

  • 缺少很多代码,例如arrallCostsverifiy() 等的定义——因此您可能需要发布minimal reproducible example。但是,您实际上似乎正在寻找一种优化算法,因此您可能会搜索它。请注意,有些精确算法可能在内存和时间方面成本更高,还有一些启发式算法成本不高,但可能无法找到最佳解决方案 - 只是一个“足够好”的解决方案。跨度>
  • 我进一步假设currectCost &gt; globalBestValue 是一个错字,你的意思是currentCost &gt; globalBestValue。此外,我们还需要知道 globalBestValue 是如何设置的,即如果它为 0 或仅包含部分成本,那么这可能是您的问题。
  • @Thomas 您好,我修正了错字并添加了很多代码,可以帮助您进一步理解我的代码。跳过 verify() 的某些部分,因为它超过 30 行并且与问题无关,但其他部分是完整的。
  • 仍有一些相关部分缺失,例如allCosts 是什么? currentValue 是如何计算的?但是,我怀疑问题出在if(currentCost &gt; globalBestValue) - 你真的想比较 costsvalue 吗?我很确定您要解决的问题有哪些变化,但我认为价值意味着更高,而成本意味着更低。最佳解决方案的成本实际上可能高于当前最佳解决方案的价值,因此将被跳过。您不想比较价值与价值以及成本与成本吗?
  • @Thomas 抱歉,这只是我的翻译。我没有分享所有代码,因为问题在于生成,而不是值。问题是,通过我的优化,回溯实际上跳过了最佳值,即 [1,0,0,1]。

标签: java recursion optimization backtracking


【解决方案1】:
  1. 请原谅我的直言不讳,但您在优化低效算法方面所做的努力只能被描述为抛光粪便。您不会通过蛮力解决任何体面大小的背包问题,而且早期返回是不够的。我已经提到了一种在 CodeReview SE 上编写高效程序的方法;这需要相当大的努力,但你必须做你必须做的。
  2. 话虽如此,我还是建议您将arr 写入控制台,以便对序列进行故障排除。看起来当您返回索引i-1 时,i 处的元素仍设置为 1,并且您估计的是上限而不是下限。以下更改可能有效:替换您的代码

    for (int i = 0; i <= 1; i++) {
        arr[fromIndex] = i;
        generate(fromIndex + 1);
    }
    

    arr[fromIndex] = 1;
    generate(fromIndex + 1);
    arr[fromIndex] = 0;
    generate(fromIndex + 1);
    

这使它变成了一种贪心算法:您实际上不是从0000000 开始,而是从1111111 开始。显然,当您存储globalBestValue 时,您应该存储提供它的实际数据。但主要建议是:当您的算法表现异常时,跟踪是您的朋友。

【讨论】:

  • 谢谢。我知道暴力破解不是最佳方式,但我在这个项目中寻找的只是优化回溯,而不是将回溯变成动态编程。我通过运行从 0 到 fromIndex 的第一个循环来修复我的优化。这种优化将我所有递归调用的数量减半。也会查看您的代码。再次感谢
猜你喜欢
  • 2019-10-25
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多