您应该坚持使用常见的装箱启发式算法。维基百科文章概述了包括链接在内的方法,适用于特定问题的确切方法。但请始终记住:这是一个NP完全问题!
我将向您展示一些支持我的提示的示例,即您应该坚持使用启发式算法。
以下Python代码:
- 创建参数化随机问题(多个平均值/标准差的正态分布;接受抽样以确保没有电影比DVD大)
- 使用某些实现某些贪婪启发式的随机装箱库(我之前没有尝试或测试过此库;因此不提供任何保证!;不知道使用哪种启发式算法)
- 使用朴素的混合整数规划实现(由商业求解器解决;开源求解器如cbc会遇到困难,但可以用于良好的近似解)
代码
import numpy as np
from cvxpy import *
from time import time
""" Generate some test-data """
np.random.seed(1)
N = 150
means = [700, 1400, 4300]
stds = [100, 300, 500]
DVD_SIZE = 4400
movies = []
for movie in range(N):
while True:
random_mean_index = np.random.randint(low=0, high=len(means))
random_size = np.random.randn() * stds[random_mean_index] + means[random_mean_index]
if random_size <= DVD_SIZE:
movies.append(random_size)
break
""" HEURISTIC SOLUTION """
import binpacking
start = time()
bins = binpacking.to_constant_volume(movies, DVD_SIZE)
end = time()
print('Heuristic solution: ')
for b in bins:
print(b)
print('used bins: ', len(bins))
print('used time (seconds): ', end-start)
""" Preprocessing """
movie_sizes_sorted = sorted(movies)
max_movies_per_dvd = 0
occupied = 0
for i in range(N):
if occupied + movie_sizes_sorted[i] <= DVD_SIZE:
max_movies_per_dvd += 1
occupied += movie_sizes_sorted[i]
else:
break
""" Solve problem """
X = Bool(N, N)
I = Bool(N)
constraints = []
for dvd in range(N):
constraints.append(sum_entries(mul_elemwise(movies, X[:, dvd])) <= DVD_SIZE)
for movie in range(N):
constraints.append(sum_entries(X[movie, :]) == 1)
for dvd in range(N):
constraints.append(sum_entries(X[:, dvd]) <= I[dvd] * (max_movies_per_dvd + 1))
objective = Minimize(sum_entries(I))
problem = Problem(objective, constraints)
start = time()
problem.solve(solver=GUROBI, MIPFocus=1, verbose=True)
end = time()
""" Print solution """
for dvd in range(N):
movies_ = []
for movie in range(N):
if np.isclose(X.value[movie, dvd], 1):
movies_.append(movies[movie])
if movies_:
print('DVD')
for movie in movies_:
print(' movie with size: ', movie)
print('Distributed ', N, ' movies to ', int(objective.value), ' dvds')
print('Optimizatio took (seconds): ', end-start)
部分输出
Heuristic solution:
-------------------
('used bins: ', 60)
('used time (seconds): ', 0.0045168399810791016)
MIP-approach:
-------------
Root relaxation: objective 2.142857e+01, 1921 iterations, 0.10 seconds
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 21.42857 0 120 106.00000 21.42857 79.8
H 0 0 68.0000000 21.42857 68.5
H 0 0 63.0000000 21.42857 66.0
0 0 21.42857 0 250 63.00000 21.42857 66.0
H 0 0 62.0000000 21.42857 65.4
0 0 21.42857 0 256 62.00000 21.42857 65.4
0 0 21.42857 0 304 62.00000 21.42857 65.4
0 0 21.42857 0 109 62.00000 21.42857 65.4
0 2 21.42857 0 108 62.00000 21.42857 65.4
40 2 27.61568 20 93 62.00000 27.61568 55.5
H 156 10 61.0000000 58.00000 4.92
262 4 59.00000 84 61 61.00000 59.00000 3.28
413 81 infeasible 110 61.00000 59.00000 3.28
H 417 78 60.0000000 59.00000 1.67
1834 1212 59.00000 232 40 60.00000 59.00000 1.67
...
...
57011 44660 infeasible 520 60.00000 59.00000 1.67
57337 44972 59.00000 527 34 60.00000 59.00000 1.67
58445 45817 59.00000 80 94 60.00000 59.00000 1.67
59387 46592 59.00000 340 65 60.00000 59.00000 1.67
分析
对上面的例子进行一些观察:
- 启发式算法立即获得价值为60的一些解决方案
- 商业求解器需要更多时间,但也找到了价值为60的解决方案(15秒)
- 也试图找到更好的解决方案或证明不存在(MIP求解器是完整的=在无限时间内找到最优解决方案或证明不存在!)
- 有一段时间没有进展!
- 但是:我们已经证明,最佳情况下只有一个大小为59的解决方案
- = 也许通过最优解决问题可以节省一张DVD;但很难找到解决方案,我们不知道这个解决方案是否存在(尚未确定!)
备注
以下观察结果严重依赖于数据统计
可以尝试其他问题(可能更小),商业MIP求解器找到的解决方案使用1个较少的DVD很容易(例如49 vs. 50)
不值得(记住:开源求解器甚至更难)
公式非常简单,没有调整(不要只责怪求解器)
有确切的算法(可能更复杂),可能是适当的
结论
通常启发式方法易于实现并且提供非常好的解决方案。大多数这些方法还具有非常好的理论保证(例如,与最优解相比最多使用11/9 opt + 1 #DVD =首次递减启发式)。尽管我通常对优化很感兴趣,但在这里我可能会使用启发式方法。
一般问题也非常流行,因此在许多编程语言中应该存在某些很好的库用于此问题!