如何解决“经典”问题?背包算法递归?
这是我的任务
背包问题是计算机科学中的经典问题。最简单的 它涉及尝试将不同重量的物品放入一个 背包,以便背包最终达到指定的总重量。 您不需要适合所有项目。例如,假设您想要 你的背包重量正好是 20 磅,你有五件物品, 重量为 11、8、7、6 和 5 磅。对于少量物品, 人类非常擅长通过检查来解决这个问题。所以你 大概可以算出只有 8、7、5 项的组合 加起来是 20。
我真的不知道从哪里开始写这个算法。我理解应用于阶乘和三角数的递归。然而我现在迷路了。
This is my task
The Knapsack Problem is a classic in computer science. In its simplest
form it involves trying to fit items of different weights into a
knapsack so that the knapsack ends up with a specified total weight.
You don't need to fit in all the items. For example, suppose you want
your knapsack to weigh exactly 20 pounds, and you have five items,
with weights of 11, 8, 7, 6, and 5 pounds. For small numbers of items,
humans are pretty good at solving this problem by inspection. So you
can probably figure out that only the 8, 7, and 5 combination of items
adds up to 20.
I really don't know where to begin writing this algorithm. I understand recursion when applied to factorials and triangle numbers. However I'm lost right now.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
你尝试了什么?
考虑到您所说的问题(指定我们必须使用递归),这个想法很简单:对于您可以采取的每一项,看看是否采取它更好。因此,只有两种可能的路径:
当您拿走该物品时,您将其从列表中删除,并根据该物品的重量减少容量。
当您不拿走该物品时,您可以从列表中删除该物品,但不会减少容量。
有时它有助于打印递归调用的样子。在本例中,它可能如下所示:
我故意显示了对 [8 7 6 5] 的调用,容量为 20,结果为 20 (8 + 7 + 5)。
请注意,[8 7 6 5] 被调用了两次:一次容量为 20(因为我们没有获取 11),一次容量为 9(因为 with 确实获取了 11)。
所以解决方案的路径:
11 未采用,调用 [8 7 6 5],容量为 20
8 采用,调用 [7 6 5],容量为 12 (20 - 8)
7 采用,调用 [6 5]容量为 5 (12 - 7)
6 的容量未被占用,调用 [5] 容量为 5
5 的容量被占用,我们的值为零。
Java 中的实际方法可以用很少的代码行来实现。
由于这显然是家庭作业,所以我只会帮助您了解一个框架:
我确实将数组复制到一个新数组,这效率较低(但无论如何,如果您寻求性能,递归不是这里的方法),但更多“功能性”。
What did you try?
The idea, given the problem you stated (which specifies we must use recursion) is simple: for each item that you can take, see if it's better to take it or not. So there are only two possible path:
When you take the item, you remove it from your list and you decrease the capacity by the weight of the item.
When you don't take the item, you remove if from you list but you do not decrease the capacity.
Sometimes it helps to print what the recursive calls may look like. In this case, it could look like this:
I did on purpose show the call to [8 7 6 5] with a capacity of 20, which gives a result of 20 (8 + 7 + 5).
Note that [8 7 6 5] is called twice: once with a capacity of 20 (because we didn't take 11) and once with a capacity of 9 (because with did take 11).
So the path to the solution:
11 not taken, calling [8 7 6 5] with a capacity of 20
8 taken, calling [7 6 5] with a capacity of 12 (20 - 8)
7 taken, calling [6 5] with a capacity of 5 (12 - 7)
6 not taken, calling [5] with a capacity of 5
5 taken, we're at zero.
The actual method in Java can fit in very few lines of code.
Since this is obviously homework, I'll just help you with a skeleton:
I did copy the array to a new array, which is less efficient (but anyway recursion is not the way to go here if you seek performance), but more "functional".
我必须为我的家庭作业执行此操作,因此我测试了上述所有代码(Python 代码除外),但它们都不适用于所有极端情况。
这是我的代码,它适用于每个极端情况。
它没有优化,递归会杀死你,但你可以使用简单的记忆来解决这个问题。为什么我的代码简短、正确且易于理解?我刚刚查看了 0-1 背包问题的数学定义 http://en.wikipedia.org/wiki/Knapsack_problem#动态编程
I had to do this for my homework so I tested all of the above codes (except for the Python one), but none of them work for every corner case.
This is my code, it works for every corner case.
It is not optimized, the recursion will kill you, but you can use simple memoization to fix that. Why is my code short, correct and simple to understand? I just checked out the mathematical definition of the 0-1 Knapsack problem http://en.wikipedia.org/wiki/Knapsack_problem#Dynamic_programming
为了简单起见,该问题基本上是经典背包问题的修改版本(没有与权重对应的值/好处)(实际情况:http://en.wikipedia.org/wiki/Knapsack_problem, 0/1 Knapsack - 对 Wiki 伪代码的一些说明< /a>, 如何理解背包问题是NP完全问题?, 为什么背包问题伪多项式?, http://www.geeksforgeeks.org/dynamic-programming-set-10-0-1-knapsack-problem/)。
以下是在 C# 中解决此问题的五个版本:
版本 1:使用动态规划(列表 - 通过急切地找到所有求和问题的解决方案以达到最终的结果)- O(n * W)
版本2:使用DP但记忆版本(懒惰 - 只是找到所需的解决方案)
版本3使用递归来演示重叠子问题和最佳子结构
版本4 > 递归(强力) - 基本上接受的答案
版本5使用#4堆栈(演示删除尾递归)
版本1:使用动态编程(列表 - 通过急切地寻找所有总和的解决方案到达最后一个的问题) - O(n * W)
版本 2:使用 DP 但记忆版本(懒惰 - 只是找到所需的解决方案)
哪里
版本3识别重叠子问题和最佳子结构
where
版本4暴力破解
版本5:使用堆栈的迭代版本(注意 - 相同的复杂性 - 使用堆栈 - 删除尾递归)
哪里
和单元测试
The problem is basically modified version of classic knapsack problem for simplicity (there are no values/benefits corresponding to weights) (for actual: http://en.wikipedia.org/wiki/Knapsack_problem, 0/1 Knapsack - A few clarification on Wiki's pseudocode, How to understand the knapsack problem is NP-complete?, Why is the knapsack problem pseudo-polynomial?, http://www.geeksforgeeks.org/dynamic-programming-set-10-0-1-knapsack-problem/).
Here are five versions of solving this in c#:
version1: Using dynamic programming (tabulated - by eagerly finding solutions for all sum problems to get to final one) - O(n * W)
version 2: Using DP but memoization version (lazy - just finding solutions for whatever is needed)
version 3 Using recursion to demonstrate overlapped sub problems and optimal sub structure
version 4 Recursive (brute force) - basically accepted answer
version 5 Using stack of #4 (demonstrating removing tail recursion)
version1: Using dynamic programming (tabulated - by eagerly finding solutions for all sum problems to get to final one) - O(n * W)
version 2: Using DP but memorization version (lazy - just finding solutions for whatever is needed)
where
version 3 Identifying overlapped sub problems and optimal sub structure
where
version 4 Brute force
version 5: Iterative version using stack (note - same complexity - using stack - removing tail recursion)
where
And unit tests
这是一个简单的递归实现(效率不是很高,但易于遵循)。它是用Python编写的,OP要求Java实现,但是将其移植到Java应该不会太困难,就像看伪代码一样。
主函数声明了三个参数:V 是值数组,W 是权重数组,C 是背包的容量。
该算法最大化添加到背包中的物品的价值,返回给定重量可达到的最大值
Here's a simple recursive implementation (not very efficient, but easy to follow). It's in Python, OP is asking for a Java implementation, but porting it to Java shouldn't be too difficult, it's like looking at pseudo-code.
The main function declares three parameters: V is an array of values, W is an array of weights and C the capacity of the knapsack.
The algorithm maximizes the value of the items added to the knapsack, returning the maximum value attainable with the given weights
Java 中的另一个动态编程实现。
我总觉得使用记忆化的自上而下的 DP 比自下而上的 DP 更容易理解。
完整、不言自明、可运行的代码,使用维基百科的此示例中的数据:
Yet another dynamic programming implementation in Java.
I always feel top-down DP using memoization is much easier to understand than bottom up DP.
Complete, self-explanatory, runnable code, using data from this example from Wikipedia:
这是另一张
Here is another one
这是一个Java解决方案
通过使用测试它
Here is a Java solution
Test it by using
这是 Java 中的一个简单的递归解决方案,但如果可能的话,您应该避免使用递归。
Here is a simple recursive solution in Java but you should avoid using recursion if possible.