1.背包问题与装箱问题

背包问题(Knapsack problem):容器数量是固定的,每个容器有自己的最大容量,而需要分配的物件有各自的价值,我们的目标是让装入容器的物体的总价值最大,并不要求所有物体都装入。背包问题有如下分类:

问题分类容器数量约束条件
0-1背包问题1个1个
多背包问题多个1个但每个背包对应不同的约束
多维背包问题1个多个

装箱问题( Bin-packing problem):容器数量不固定,每个容器容量是相同的,有自己的最大容量,我们的目标是用最少的容器来存放所有的物件。装箱问题也可以分为多种情况,如完全装箱问题、不完全装箱问题、一维装箱问题和二维装箱问题等。

2.背包问题建模

2.1 0-1背包问题建模

实例:OR-Tools官网题目

50个物品被装入一个箱子。每个物品都有一个值(物品上的数字)和一个重量(与物品的面积大致成比例)。背包的容量被为850,目标是找到一组能使总价值最大化而又不超过容量的物品。

python调用ortools求解:

from ortools.linear_solver import pywraplp

values = [
    360, 83, 59, 130, 431, 67, 230, 52, 93, 125, 670, 892, 600, 38, 48, 147,
    78, 256, 63, 17, 120, 164, 432, 35, 92, 110, 22, 42, 50, 323, 514, 28,
    87, 73, 78, 15, 26, 78, 210, 36, 85, 189, 274, 43, 33, 10, 19, 389, 276,
    312
]
weights = [
    7, 0, 30, 22, 80, 94, 11, 81, 70, 64, 59, 18, 0, 36, 3, 8, 15, 42, 9, 0,
    42, 47, 52, 32, 26, 48, 55, 6, 29, 84, 2, 4, 18, 56, 7, 29, 93, 44, 71,
    3, 86, 66, 31, 65, 0, 79, 20, 65, 52, 13
]
W = 850  # 背包容量
num_items = len(weights)  # 物件数量 J

# 建模
solver = pywraplp.Solver.CreateSolver("SCIP")

# 决策变量
x = {}
for j in range(num_items):
    x[j] = solver.IntVar(0, 1, f"x{j}")

# 约束条件
cn_terms = []
for j in range(num_items):
    cn_terms.append(weights[j] * x[j])
solver.Add(solver.Sum(cn_terms) <= W)


# 目标函数
objective_terms = []
for j in range(num_items):
    objective_terms.append(values[j] * x[j])
solver.Maximize(solver.Sum(objective_terms))

status = solver.Solve()

if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
    print(f"Total value = {solver.Objective().Value()}")
    total_weight = 0
    picked_items = []
    for j in range(num_items):
        if x[j].solution_value() > 0.5:
            total_weight += weights[j]
            picked_items.append(j)
    print(f"total weight={total_weight}")
    print(f"装入了背包的物品:{picked_items}")

else:
    print("No solution found.")

结果输出:

Total value = 7534.0
total weight=850
装入了背包的物品:[0, 1, 3, 4, 6, 10, 11, 12, 14, 15, 16, 17, 18, 19, 21, 22, 24, 27, 28, 29, 30, 31, 32, 34, 38, 39, 41, 42, 44, 47, 48, 49]

2.2 多背包问题建模

约束2:限制每个物件只能装入一个背包

python调用ortools求解

from ortools.linear_solver import pywraplp

weights = [48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36] w
values = [10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25] v
W = [100, 100, 100, 100, 100]  # 5个背包的容量 W

num_items = len(weights)  # 物件数量 J
num_knapsacks = len(W)  # 背包数量 I

# 建模
solver = pywraplp.Solver.CreateSolver("SCIP")

# 决策变量
x = {}
for i in range(num_knapsacks):
    for j in range(num_items):
        x[i,j] = solver.IntVar(0, 1, f"x{i}{j}")

# 约束条件

# 目标函数

 结果输出:

Total value = 395.0
背包0的total weight=96
装入了背包0的物品是:[1, 10, 14]
背包1的total weight=192
装入了背包1的物品是:[8, 9, 13]
背包2的total weight=276
装入了背包2的物品是:[7, 12]
背包3的total weight=348
装入了背包3的物品是:[3, 4]
背包4的total weight=438
装入了背包4的物品是:[2, 5]

2.3 多维背包问题

python调用ortools求解

from ortools.linear_solver import pywraplp

# ==========测试==========
knapsack = {"weight": 600, "volume": 600}
values = [1898, 440, 22507, 270, 14148, 3100, 4650, 30800, 615, 4975, 1160, 4225, 510, 11880, 479, 440, 490, 330, 110,
          560, 24355, 2885, 11748, 4550, 750, 3720, 1950, 10500]

weights = [45, 5, 85, 150, 65, 95, 30, 12, 170, 20, 40, 25, 20, 3, 7, 25, 12, 22, 25, 9, 165, 2, 85, 15, 9, 2, 4, 100]
volumes = [30, 20, 125, 5, 80, 25, 35, 73, 12, 15, 15, 40, 5, 10, 10, 12, 10, 9, 10, 20, 60, 40, 50, 36, 49, 40, 19,
           150]
dimension_items = {
    "weight": weights,
    "volume": volumes
}

solver = pywraplp.Solver.CreateSolver("SCIP")

num_dimensions = len(dimension_items)
num_items = len(values)
# 0-1决策变量
x = {}
for j in range(num_items):
    x[j] = solver.IntVar(0, 1, f'x{j}')

# 约束条件

# 目标函数

 结果输出:

Total cost = 140668.0

背包—放入的物品: [2, 4, 6, 7, 9, 10, 11, 13, 20, 22, 23, 25, 26]

3.完全装箱问题建模 

 约束2:限制每个物件只能装入一个箱子

python调用ortools求解

from ortools.linear_solver import pywraplp

weights = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 30]  # w_j
num_items = len(weights)  # 物件集合J
num_bins = num_items  # 箱子集合I
W = 100  # 箱子的容量

solver = pywraplp.Solver.CreateSolver("SCIP")

# 初始化决策变量
# Variables
# x[i, j] = 1 if item i is packed in bin j.
x = {}
for i in range(num_bins):
    for j in range(num_items):
        x[i, j] = solver.IntVar(0, 1, f"x{i}{j}")

# y[j] = 1 if bin j is used.
y = {}
for i in range(num_bins):
    y[i] = solver.IntVar(0, 1, f"y{i}")

# 约束条件

# 目标函数

结果输出:

num of bins = 4.0

箱子0-装入物品:[0, 1, 2]
箱子0-总重量:97
箱子1-装入物品:[3, 4, 5]
箱子1-总重量:99
箱子2-装入物品:[6, 7]
箱子2-总重量:84
箱子3-装入物品:[8, 9, 10]
箱子3-总重量:90

4.相关阅读

Google-OR-Tools-pack

OR-Tools:2-包装问题,箱包问题(bin packing)

这里有个疑问,相关阅读中用的CBC求解器和本文中采用的SCIP求解出来的结果不同,待研究

相关视频会在后续发布,欢迎关注我的bilibili:无形忍者的个人空间-无形忍者个人主页-哔哩哔哩视频

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐