[Python3]BFS优先队列分枝限界法求解0/1背包问题

感觉网络上关于优先队列分枝限界法求解0/1背包问题的python代码很少,下面给出一些自己学习过程中的心得体会,(以下代码是从各路大神那里学来的)以及详细代码解释,如有错误请务必留言指正!先给出代码:

import queue
class Node:
    def __init__(self,level,weight,value,bound):
        self.level = level
        self.value = value
        self.weight = weight
        self.bound = bound
    def __lt__(self,other):
        return self.bound < other.bound
    
class Solution:   
    def BFS(self,weights,values,W,n):
        max_value = 0
        priority_queue = queue.PriorityQueue()
        root = Node(-1,0,0,0)
        priority_queue.put(root)

        while not priority_queue.empty():
            node = priority_queue.get()

            if node.level < n-1:
                level = node.level + 1
                
                next_weights = node.weight + weights[level]
                next_values = node.value + values[level]
                
                if next_weights <= W and next_values > max_value:
                    max_value = next_values
                
                bound_value = node.value + (W - node.weight) * (values[level]/weights[level])
                
                if bound_value > max_value:
                    lnode = Node(level,next_weights,next_values,bound_value)
                    priority_queue.put(lnode)
                
                rnode = Node(level,node.weight,node.value,node.bound)
                priority_queue.put(rnode)
        return max_value

weights = [4, 7, 5, 3]
values = [40, 42, 25, 12]
W = 10
n = 4
b = Solution()
a = b.BFS(weights,values,W,n)
print(a)

前情提要:import了queue这个包,主要是为了调用优先队列这个库函数,这个库函数写开就是一个大顶堆,如果有能力希望能够自己写这个函数,而不是直接调用库函数。

一、结构定义:

        定义了Node类,并且初始化了level,value,weight,bound,分别对应层数(头结点为-1层),价        值,重量,以及上界。

        __lt__是python中的magic函数,当self.bound < other.bound的时候返回True,否则返回False,用来维持queue.PriorityQueue这个优先队列中结点的值的优先级,bound值越大的排在前面,优先级越高。

class Node:
    def __init__(self,level,weight,value,bound):
        self.level = level
        self.value = value
        self.weight = weight
        self.bound = bound
    def __lt__(self,other):
        return self.bound < other.bound

二、函数解释:

1)我们采用二叉树的BFS来搜索最合适的结点,我们定义了BFS函数,将最大价值(max_value)设置为0,实例化一个优先队列(queue.PriorityQueue),这个优先队列实际上是由堆来维持最大值的,实例化根节点,只传入根节点(根节点的level=-1),使用.put()将根节点传入优先队列中。

def BFS(self,weights,values,W,n):
        max_value = 0
        priority_queue = queue.PriorityQueue()
        root = Node(-1,0,0,0)
        priority_queue.put(root)

2)下面是while内的逐行解释(从第一次循环开始):

1.使用一个while循环,当优先队列不为空的时候,使用.get()从优先队列中获取结点。

2.如果还未搜索到叶子结点(node.level < n-1),则进入到下一层,对level+1(始终记得根节点是-1)。

3.此时对第二层的结点的重量以及价值进行一个判断,node.weight是根节点的重量为0,weights[level]为第二个结点的重量(我们输入的weights是一个列表,其中的索引为[0,1,2,3],因此此时加的是索引为1的这个值), 对value做同样的操作.

4.if语句判断next_weights和next_values是否超过最大重量W和最大价值,如果没有超过最大重量,并且当前价值超过了最大价值,那么把当前的最大价值赋值给max_value。

5.bound_value这里是一个限界函数,具体解释如下:

如果所有剩余的物品不能全部装入背包,那么价值的上界 

       node.value=node.value+ (value[i+1]+…+value[k])+(物品k+1装入的部分重量)× 物品k+1的单位价值,这样每个结点实际装入背包的价值一定小于等于该上界。 

同时这个限界函数给出每一个可行结点相应的子树可能获得的最大价值的上界。如 果这个上界不比当前最优值更大,则说明相应的子树中不含问题的最优解, 因此该结点可以剪去(剪枝)。

6.如果这个限界值bound_value大于max_value,则说明,这个结点可以进行扩展,是有可能找到更优解的,因此将其存入优先队列,并且Node中传入更新后的重量,价值,level,以及限界值。

如果bound_value <= max_value则什么都不做(相当于剪枝了).

最后未选择的结点我们不改变重量,价值以及限界值,只改变level,将其存储优先队列。

注意:此时优先队列中始终维护传入队列的结点的最大值为第一个值,因为最大的值更有可能作为扩展对象,可以增大搜索效率。

最后while循环结束传入max_value

while not priority_queue.empty():
            node = priority_queue.get()

            if node.level < n-1:
                level = node.level + 1
                
                next_weights = node.weight + weights[level]
                next_values = node.value + values[level]
                
                if next_weights <= W and next_values > max_value:
                    max_value = next_values
                
                bound_value = node.value + (W - node.weight) * (values[level]/weights[level])
                
                if bound_value > max_value:
                    lnode = Node(level,next_weights,next_values,bound_value)
                    priority_queue.put(lnode)
                
                rnode = Node(level,node.weight,node.value,node.bound)
                priority_queue.put(rnode)
        return max_value

3)进行测试

weights = [4, 7, 5, 3]
values = [40, 42, 25, 12]
W = 10
n = 4
b = Solution()
a = b.BFS(weights,values,W,n)
print(a)

测试的最终结果为65。
 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用分支限界求解0/1背包问题Python代码: ```python class Item: def __init__(self, value, weight): self.value = value self.weight = weight self.ratio = value / weight class Node: def __init__(self, level, value, weight, items): self.level = level self.value = value self.weight = weight self.items = items def __lt__(self, other): return self.value < other.value def knapsack_bnb(items, capacity): items = sorted(items, key=lambda x: x.ratio, reverse=True) root = Node(-1, 0, 0, []) queue = [] queue.append(root) best_value = 0 while queue: node = queue.pop(0) if node.level == len(items) - 1: if node.value > best_value: best_value = node.value best_node = node else: if node.weight + items[node.level+1].weight <= capacity: left_node = Node(node.level+1, node.value+items[node.level+1].value, node.weight+items[node.level+1].weight, node.items+[items[node.level+1]]) queue.append(left_node) bound = bound_value(items, capacity, node) if bound > best_value: right_node = Node(node.level+1, node.value, node.weight, node.items) queue.append(right_node) return best_node def bound_value(items, capacity, node): if node.weight >= capacity: return 0 bound = node.value j = node.level + 1 tot_weight = node.weight while j < len(items) and tot_weight + items[j].weight <= capacity: bound += items[j].value tot_weight += items[j].weight j += 1 if j < len(items): bound += (capacity - tot_weight) * items[j].ratio return bound # example usage items = [Item(20, 10), Item(30, 20), Item(66, 30), Item(40, 40), Item(60, 50)] capacity = 100 best_node = knapsack_bnb(items, capacity) print("Best value:", best_node.value) print("Items included:") for item in best_node.items: print(" - value:", item.value, "weight:", item.weight) ``` 在上述代码中,`Item`和`Node`分别表示物品和节点。`knapsack_bnb`函数是求解0/1背包问题的主函数。它首先将物品按照价值重量比从大到小排序,然后从根节点开始,以BFS的方式依次遍历所有子节点。对于每个节点,它有两个子节点,一个是选择当前物品,另一个是不选择当前物品。选择当前物品的子节点的价值和重量是在父节点的基础上加上当前物品的价值和重量,而不选择当前物品的子节点的价值和重量与父节点相同。为了提高效率,在扩展子节点之前,我们首先计算一个上界,如果当前节点的上界小于当前最优解,那么就不需要扩展该节点的右子节点了。`bound_value`函数用于计算节点的上界。 在上述代码的最后,我们用一个例子来演示如何使用该函数。给定5个物品和一个容量为100的背包,我们可以得到最优的价值为146和所选物品为2、3、4、5。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值