Python中的背包问题

2025年1月5日 | 阅读6分钟

在本教程中,我们将学习如何用 Python 解决背包问题。我们将使用几种方法来找到解决方案。在这个问题中,我们有一组 N 件物品,每件物品都有其重量和价值。我们的目标是以一种最大化背包中物品总价值的方式,选择物品并将它们放入一个容量有限的背包中,该容量表示为 W。需要强调的是,我们只能选择每件物品一次。

换句话说,我们提供了两个数组:一个包含价值 (val),另一个包含 N 件物品的重量 (wt)。此外,还有一个整数 W,表示背包的最大承重。我们的任务是确定 val 数组中最有价值的物品组合,同时确保 wt 数组中对应重量的总和不超过背包的容量。请注意,您不能分割物品;您可以将其完全放入您的选择中,或者将其丢弃。这个问题通常被称为 0/1 背包问题。

示例 1

输入

输出

9

解释:为了在背包容量为 6 的情况下最大化价值,请选择第一件价值为 5 的物品和第四件价值为 4 的物品。

示例 2

输入

输出

12

解释:选择第二件和第五件物品,在背包容量允许的情况下获得最大的总价值 12。

让我们用各种方法来解决上述问题。

方法 1:蛮力法

首先,我们将使用蛮力法来解决这个问题。使用此方法解决 0/1 背包问题涉及生成所有可能的物品组合,并计算每种组合的总价值。然后,我们选择不超出背包容量且具有最大价值的组合。以下是此蛮力法的 Python 实现。

示例 -

输出

Maximum Value: 3
Selected Items: [0, 0, 1]

解释 -

让我们来理解代码的以下步骤。

  1. 首先,我们定义一个名为 knapsack_bruteforce() 的函数,该函数接受三个参数:values、weights 和 capacity。这些参数分别表示物品的价值、重量和背包的容量。
  2. 在函数内部,它计算 `values` 列表的长度(物品数量)并将其分配给变量 n。
  3. 然后我们在主函数内定义 get_total_value() 函数。此内部函数计算由二进制列表表示的物品组合的总价值和总重量。
  4. 它初始化变量 best_combination(一个零列表,表示未选择任何物品)和 best_value(初始化为 0),以跟踪迄今为止找到的最佳组合。
  5. 然后它进入一个循环,该循环生成所有可能的物品组合。它使用从 0 到 2^n(n 是物品数量)的数字的二进制表示来指示物品选择。
  6. 对于每种组合,它会调用 get_total_value() 函数来计算该组合的总价值和总重量。
  7. 它检查组合是否可行(总重量不超过背包容量)以及它是否比迄今为止找到的最佳组合具有更高的总价值。
  8. 如果满足这些条件,它会更新 best_combination 和 best_value 以及当前组合及其总价值。
  9. 在检查完所有可能的组合后,该函数返回最大总价值(`best_value`)和相应的选定物品组合(`best_combination`)。

请记住,由于其指数级时间复杂度,蛮力法对于背包问题的较大实例效率低下。为了克服这一点,我们将使用动态规划进行高效的解决方案。

方法 2:动态规划法

让我们理解以下示例 -

示例 -

输出

Maximum Value: 3

解释 -

让我们来理解使用动态规划的 0/1 背包问题的以下代码。

  1. 定义了 knapsack_max_value() 函数来查找在给定背包容量内可以获得的最大价值,它接受三个参数 - 第一个参数 capacity 代表背包的最大承重。第二个参数 weight 是一个代表物品重量的列表,values 是一个代表物品价值的列表。
  2. 变量 n 初始化为 values 列表的长度,表示物品的数量。
  3. 创建一个二维列表 dp 来存储不同物品组合和容量实现的最高价值。它有 (n + 1) 行和 (capacity + 1) 列。
  4. 动态规划算法以自下而上的方式构建 dp 表,考虑所有可能的物品组合和容量。
  5. 两个嵌套循环遍历物品数量 (i) 和可用容量 (w)。对于每种组合
    1. 如果剩余的物品数量为零 (`i == 0`) 或背包的容量为零 (`w == 0`),则最大值为 0。
    2. 如果当前物品的重量 (`weights[i-1]`) 小于或等于可用容量 (`w`),则算法通过考虑两个选项来计算最大值:包含当前物品和不包含当前物品。
    3. 如果当前物品的重量大于可用容量,则最大值与不包含此物品所获得的最大值相同。
  6. 该函数返回动态规划过程结束时获得的最大值,该值存储在 `dp[n][capacity]` 中。
  7. 在示例用法中,代码演示了如何使用一组示例值、重量和背包容量来使用 knapsack_max_value() 函数。它会打印出在给定约束条件下可以达到的最大值。

动态规划方法通过考虑所有可能的组合并选择在满足背包容量要求的情况下价值最大的组合,从而有效地解决了 0/1 背包问题。

方法 3:贪心算法

贪心算法是一种用于解决优化问题的技术,其目标是根据一组特定标准发现最佳解决方案。这些算法在每一步都做出看似最有利的决策,期望这些局部最优选择最终能带来最有利的整体解决方案。

示例 -

输出

Maximum Value (Greedy Approach): 3
Selected Items: [1, 0, 0]

结论

在本教程中,我们探讨了在 Python 中解决 0/1 背包问题的各种方法。我们讨论了蛮力法,该方法系统地生成所有可能的物品组合;动态规划法,该方法使用二维表高效地计算最优解;以及贪心算法,该算法基于价值-重量比做出局部最优选择。

动态规划法是最有效的,而贪心算法则提供了一种快速但不总是最优的解决方案。方法选择取决于问题的大小以及对最优解或启发式解决方案的需求。