多背包问题

多背包问题

已知:运送一批物品,质量和需求数量如下:
   质量: 26, 30, 40, 56, 60, 76, 80, 86, 90, 100, 110, 116, 120, 176, 180, 200
   数量: 2, 10, 8, 16, 80, 6, 32, 4, 36, 4, 10, 8, 18, 40, 16, 8

求解:最小需要承重为600的背包多少个?

解答:

  • 贪心算法 + 整数规划
  • 动态规划 + 整数规划

贪心算法 + 整数规划

  • 整体思路:
      一个背包一个背包的装入,每次将背包最大程度的装满(整数规划),直至所需物品全部装入。
  • 贪心算法:
      每次将背包最大程度的装满
  • 整数规划:
      数量, x 1 , x 2 , . . . , x 22 x_1,x_2,...,x_{22} x1,x2,...,x22
      maximize:   26 ∗ x 1 + 29 ∗ x 2 + . . . + 201 ∗ x 22 26 * x_1 + 29 * x_2 + ... + 201 * x_{22} 26x1+29x2+...+201x22
      S.T.:
         26 ∗ x 1 + 29 ∗ x 2 + . . . + 201 ∗ x 22 ≤ 600 26 * x_1 + 29 * x_2 + ... + 201 * x_{22} \leq 600 26x1+29x2+...+201x22600
         0 ≤ x 1 ≤ 2 , i n t e g e r 0 \leq x_1 \leq 2, integer 0x12,integer
         0 ≤ x 2 ≤ 10 , i n t e g e r 0 \leq x_2 \leq 10, integer 0x210,integer
        …
         0 ≤ x 22 ≤ 8 , i n t e g e r 0 \leq x_{22} \leq 8, integer 0x228,integer
import numpy as np
import pulp as lp


def integer_program(weights, nums, max_weight):
    """
    整数规划
    input:
        weights: 物品质量集
        nums: 物品需求数量集
        max_weight: 背包承重
    output:
        单个背包装入数量集
    """
    prob = lp.LpProblem("The GY Problem", lp.LpMaximize)
    x = []
    for i, n in enumerate(nums):
        x.append(lp.LpVariable("x_%03d" % i, lowBound=0, upBound=n, cat="Integer"))

    prob += lp.lpSum([weights[i] * x[i] for i in range(len(x))]), "Total weight"
    prob += lp.lpSum([weights[i] * x[i] for i in range(len(x))]) <= max_weight, "Max weight"
    prob.solve()
#     print(prob)
#     print("\tStatus:", lp.LpStatus[prob.status])
    return np.array([v.varValue for v in prob.variables()]).astype("int32")


# 初始化变量
weights_array = np.array(
    [26, 30, 40, 56, 60, 76, 80, 86, 90, 100, 110, 116, 120, 176, 180, 200])
nums_array = np.array(
    [2, 10, 8, 16, 80, 6, 32, 4, 36, 4, 10, 8, 18, 40, 16, 8])
bag_weight = 600

# 理论最少背包数
bags_num_min = np.ceil(np.sum(weights_array * nums_array) / bag_weight)
print("理论最少", bags_num_min)

# 贪心算法
bags_num = 0
bag_nums_list = []   # 每个背包装入物品方案集合
while np.any(nums_array > 0):
    nums_array_ = integer_program(weights_array, nums_array, bag_weight)
    nums_array -= nums_array_
    bags_num += 1
    bag_nums_list.append(nums_array_)
print("实际最少", bags_num)
理论最少 49.0
实际最少 51

局部最优,未必是全局最优


动态规划 + 整数规划

  • 整体思路:
      整体考虑,先动态规划求解所有可能的背包装满方案(不能再放入任一物品),再进行整数规划。
      整数规划过程中,可能会存在长时间获取不到最优解,解决方式:
      1. 改变方案集合数量(增加或减少);
      2. 贪心算法减少方案集合数量。
  • 动态规划:
      求解所有可能的背包装满方案(不能再放入任一物品)。
  • 整数规划:
      方案, c 1 , c 2 , . . . , c n c_1,c_2,...,c_n c1,c2,...,cn
      每个方案采用数量, x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn
      各个方案物品分布数量, A 1 i , A 2 i , . . . , A n i A_{1i},A_{2i},...,A_{ni} A1i,A2i,...,Ani
      每种物品需求数量, b i bi bi
      minimize:   x 1 + x 2 + . . . + x n x_1 + x_2 + ... + x_n x1+x2+...+xn
      S.T.:
         A 1 i ∗ x 1 + A 2 i ∗ x 2 + . . . + A n i ∗ x 22 ≥ b i A_{1i} * x_1 + A_{2i} * x_2 + ... + A_{ni} * x_{22} \geq {bi} A1ix1+A2ix2+...+Anix22bi
         0 ≤ x 1 , i n t e g e r 0 \leq x_1, integer 0x1,integer
         0 ≤ x 2 , i n t e g e r 0 \leq x_2, integer 0x2,integer
        …
         0 ≤ x n , i n t e g e r 0 \leq x_n, integer 0xn,integer
from copy import deepcopy
import numpy as np
import pulp as lp


def bag_program(weights, nums, max_weight):
    """
    动态规划,获取所有可能的背包装满方案
    input:
        weights: lengths array
        nums: numbers array
        max_weight: pipe length
    output:
        program
    """
    weight_num = [[]] * max_weight

    for il, l in enumerate(weights):
        for i in range(max_weight):
            if weight_num[i]:
                if i + l < max_weight:
                    vals = deepcopy(weight_num[i])
                    vals_ = []
                    for val in vals:
                        val[il] += 1
                        if val[il] <= nums[il]:
                            vals_.append(val)
                    tmp = deepcopy(weight_num[i + l])
                    tmp.extend(vals_)
                    weight_num[i + l] = tmp
        val = np.zeros(len(weights))
        for i in range(l, max_weight + 1, l):
            val[il] += 1
            if val[il] > nums[il]:
                break
            tmp = deepcopy(weight_num[i - 1])
            tmp.append(deepcopy(val))
            weight_num[i - 1] = tmp
    return weight_num[-weights[0]:]


def integer_program(num_arrs, nums):
    """
    input:
        num_arrs: num set
        nums: minimize num
    output:
        result
    """
    num_arrays_list = []
    for vals in num_arrs:
        num_arrays_list.extend(vals)

    nums_mat = np.array(num_arrays_list)
    print("\t", nums_mat.shape)

    A = nums_mat.T
    b = nums

    prob = lp.LpProblem("The GY Problem", lp.LpMinimize)
    x = [lp.LpVariable("x_%06d" % i, lowBound=0, cat="Integer")
         for i in range(A.shape[1])]

    prob += lp.lpSum(x), "Total Number"
    for i in range(len(b)):
        prob += lp.lpSum([A[i][j] * x[j] for j in range(A.shape[1])]) >= b[i], "lb%04d" % weights_array[i]
    prob.solve()
    print("\tStatus:", lp.LpStatus[prob.status])

    res = []
    for v in prob.variables():
        if v.varValue:
            res.append((A[:, int(v.name[2:])], v.varValue))
    return res


# 初始化变量
weights_array = np.array(
    [26, 30, 40, 56, 60, 76, 80, 86, 90, 100, 110, 116, 120, 176, 180, 200])
nums_array = np.array(
    [2, 10, 8, 16, 80, 6, 32, 4, 36, 4, 10, 8, 18, 40, 16, 8])
bag_weight = 600

# 动态规划获取所有可能的背包装满方案(不能再放入任一物品)
weight_num_arrays = bag_program(weights_array, nums_array, bag_weight)

# 理论最少背包数
bags_num_min = np.ceil(sum(weights_array * nums_array) / bag_weight)
print("理论最少", bags_num_min)

# 迭代进行整数规划
# 迭代?方案数量较多,未知数多,求解慢
# 从较少方案集入手,慢慢增加方案集,直至获得最优解
for i in range(1, len(weight_num_arrays) + 1):
    print(i)
    # 方案数量为0,与上次迭代没变化
    if not weight_num_arrays[-i]:
        continue
    result = integer_program(weight_num_arrays[-i:], nums_array)
    bags_num = sum([n[-1] for n in result])
    print("\t", bags_num)
    # 当达到理论最小数量时,已获得最优解决方案
    if bags_num == bags_num_min:
        break
print("实际最少", sum([n for _, n in result]))

# # 承重验证
# np.all([sum(v * weights_array) <= bag_weight for v,_ in result])
# # 数量验证
# np.all(np.sum(np.array([v * n for v, n in result]), axis=0) - nums_array >= 0)
理论最少 49.0
1
	 (7122, 16)
	Status: Optimal
	 50.0
2
3
	 (16896, 16)
	Status: Optimal
	 49.0
实际最少 49.0

全局最优,求解慢

你可能感兴趣的:(整数规划,贪心算法,动态规划,且走且看)