01背包问题
01背包问题是最基本的背包问题,在背包9讲里面是怎么描述这个问题的呢?
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。
求解将哪些物品装入背包可使价值总和最大。
这个问题里面物品是不能重复的
所以对于这样的问题称为01背包问题,也是对于第i个物品到底是选择(1)还是不选择(0)的问题。
动态规划的思路做的话,可以用Top-Down和Bottom-Up的两种思路,不过逻辑都是一样的。
看一下递归公式
𝑓[𝑖][𝑗]=𝑚𝑎𝑥𝑓[𝑖−1][𝑗],𝑓[𝑖−1][𝑗−𝑐[𝑖]]+𝑤[𝑖]
公式可以拆分成两项:
第一项,不选择c[i],那么直接采用前 𝑖−1 物品时候的结果(最优子问题)
𝑓[𝑖][𝑗]=𝑓[𝑖−1][𝑗]
第二项,如果选择了c[i],那么就会选择前i-1个的最优解和目前结果相加。
𝑓[𝑖][𝑗]=𝑓[𝑖−1][𝑗−𝑐[𝑖]]+𝑤[𝑖]
当然最后需要取这两项的最大值
下面主要是两个版本的代码
0-1 Knapsack Problem | DP-10 - GeeksforGeekswww.geeksforgeeks.org/0-1-knapsack-problem-dp-10/编辑
Top-Down
# A naive recursive implementation
# of 0-1 Knapsack Problem
# Returns the maximum value that
# can be put in a knapsack of
# capacity W
def knapSack(W, wt, val, n):
# Base Case
if n == 0 or W == 0 :
return 0
# If weight of the nth item is
# more than Knapsack of capacity W,
# then this item cannot be included
# in the optimal solution
if (wt[n-1] > W):
return knapSack(W, wt, val, n-1)
# return the maximum of two cases:
# (1) nth item included
# (2) not included
else:
return max(
val[n-1] + knapSack(
W-wt[n-1], wt, val, n-1),
knapSack(W, wt, val, n-1))
# end of function knapSack
# To test above function
val = [60, 100, 120]
wt = [10, 20, 30]
W = 50
n = len(val)
print knapSack(W, wt, val, n)
# This code is contributed by Nikhil Kumar Singh
Bottom-Up
# A Dynamic Programming based Python
# Program for 0-1 Knapsack problem
# Returns the maximum value that can
# be put in a knapsack of capacity W
def knapSack(W, wt, val, n):
K = [[0 for x in range(W + 1)] for x in range(n + 1)]
# Build table K[][] in bottom up manner
for i in range(n + 1):
for w in range(W + 1):
if i == 0 or w == 0:
K[i][w] = 0
elif wt[i-1] <= w:
K[i][w] = max(val[i-1]
+ K[i-1][w-wt[i-1]], K[i-1][w])
else:
K[i][w] = K[i-1][w]
return K[n][W]
# Driver program to test above function
val = [60, 100, 120]
wt = [10, 20, 30]
W = 50
n = len(val)
print(knapSack(W, wt, val, n))
# This code is contributed by Bhavya Jain
完全背包问题
有N种物品和一个容量为V的背包,每种物品都有无限件可用。
第i种物品的费用是c[i],价值是w[i]。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这个问题和上面问题的唯一区别就是在取物品的时候,不再是唯一的(01)的了,而可以是无限制的。
看了网上的一些思路,始终不太明白,为什么第二项从 𝑓[𝑖−1][𝑗−𝑐[𝑖]] 变成了 𝑓[𝑖][𝑗−𝑐[𝑖]] 之后,01背包问题就变成了完全背包问题。
𝑓[𝑖][𝑗]=𝑚𝑎𝑥𝑓[𝑖−1][𝑗],𝑓[𝑖−1][𝑗−𝑐[𝑖]]+𝑤[𝑖] (01背包问题)
𝑓[𝑖][𝑗]=𝑚𝑎𝑥𝑓[𝑖−1][𝑗],𝑓[𝑖][𝑗−𝑐[𝑖]]+𝑤[𝑖] (完全背包问题)
下面是我的解释:
在完全背包问题里,左右都是f[i],所以随着j的增加,f[i][j]是可以通过f[i][j - c[i]]来累加c[i]的。
这里最简单的是f[i] = f[i] + n这个公式可以不断循环
for i in range(n):
f(i) = f(i) + n
可知道f(n) = n^2,所以每次都累加n
如果右边不是f(i)那么就没办法累加,而是每次都算一下f[i][j]相比于f[i-1][j]增加了多少。
而在01问题里面,f[i][j]在j只能选择一个c[i],因为f[i - 1][j - c[i]]是前i - 1项物品c[1]...c[i - 1]的最优子问题。
这里可以举一个最简单的列子(完全背包问题)
对于商品(2, 2,0)(1,5,1)(重量,价值,i) W=10
在计算f[1]的时候,可以知道f[1]=0,2,2,4,4,6,6,8,8,10
在计算f[2][2]的时候,可以知道f[2][1]=5
而这个时候,f[2][2]如果还是取i-1的话,每次都只可以取2,不能够在自身的基础上累加。
如果取f[i]就可以每次对自身进行累加f[2][2] = 5 + 5(此时已经去了两次)
最终的f[2]=5,10,15,20,25...
空间复杂度的简化
对于上面的01背包问题,可以得知,其空间复杂度是 (物品数量X限定容量)
对于完全背包问题也一样
所以为了将空间复杂度简化成o(限定容量)
在01背包问题中,取得f[j]就必须是f[j - 1],如果是顺序的方法去取得话,那么f[j]保存的一定是f[j - c[j]]这个一维数组里面的值一定会被取代成f[i][j - c[i]],所以逆向的话,f[j]每次都是取小的f[j-c[i]],都会取到以前的结果。
for i=1..N
for j=V..0
f[j]=max{f[j],f[j-c[i]]+w[i]};
同理,在完全背包问题中,f[j]应该每次都取到的是f[i][j - c[i]]的值。只有是顺序的情况。
完结!
```python
class BertPooler(nn.Module):
def __init__(self, config):
super().__init__()
self.dense = nn.Linear(config.hidden_size, config.hidden_size)
self.activation = nn.Tanh()
def forward(self, hidden_states):
# We "pool" the model by simply taking the hidden state corresponding
# to the first token.
first_token_tensor = hidden_states[:, 0]
pooled_output = self.dense(first_token_tensor)
pooled_output = self.activation(pooled_output)
return pooled_output
from transformers.models.bert.configuration_bert import *
import torch
config = BertConfig.from_pretrained("bert-base-uncased")
bert_pooler = BertPooler(config=config)
print("input to bert pooler size: {}".format(config.hidden_size))
batch_size = 1
seq_len = 2
hidden_size = 768
x = torch.rand(batch_size, seq_len, hidden_size)
y = bert_pooler(x)
print(y.size())
```