我有许多不同大小(宽度和高度)的'砖'和一个固定大小的容器。我想从顶部向下移动,尽可能紧凑地布置容器内的砖块。在给定前面的步骤的情况下,我已经选择了网格在任何步骤都应该是最佳的标准。到目前为止,我有以下(低效)代码不起作用:
def fits?(x, y, w, h)
!((x + w > W) || (y + h > H))
end
def overlaps?(existing, modified)
existing[:x] + existing[:w] > modified[:x] && existing[:y] + existing[:h] > modified[:y] && modified[:x] + modified[:w] > existing[:x] && modified[:y] + modified[:h] > modified[:y]
end
AXIS = :x
W = 800
H = 6400
sizes = [
{ w: 200 , h: 200 },
{ w: 200 , h: 200 },
{ w: 400 , h: 400 },
{ w: 200 , h: 200 },
{ w: 200 , h: 200 },
{ w: 400 , h: 400 },
{ w: 600 , h: 600 },
{ w: 200 , h: 200 },
{ w: 800 , h: 800 },
]
existing = []
sizes.each do |size|
size[:x] = 0
size[:y] = 0
existing.each do |existing|
if overlaps?(size, existing)
if fits?(x = existing[:x] + existing[:w], y = existing[:y], size[:w], size[:h])
size[:x] = x
size[:y] = y
redo
end
if fits?(x = existing[:x], y = existing[:y] + existing[:h], size[:w], size[:h])
size[:x] = x
size[:y] = y
redo
end
case AXIS
when :x then size[:x] = 0; size[:y] = existing[:y] + existing[:h]
when :y then size[:y] = 0; size[:x] = existing[:x] + existing[:w]
end
end
end
puts "#{size[:x]} , #{size[:y]} , #{size[:w]} , #{size[:h]}"
existing << size
end
我有什么想法可以解决这个问题?这似乎是贪婪算法的一个主要例子,但我无法弄清楚应该是什么。
答案 0 :(得分:4)
您的问题是 NP-Hard ,因此没有已知的多项式解决方案。
可以显示Subset Sub Problem的简单缩减:
广义子集和问题:给定集合S
和整数k
,当且仅当存在S
的子集时才返回true到k
。
缩减:给定子集sum (S,k)
的实例 - 创建一个大小为(1,k)
的容器,每个(1,s)
的元素为s
} S
。
很容易看出,当且仅当你能完全填充容器时 - 原始子集和问题的解决方案是正确的,因此上面是多项式减少,问题是NP-Hard。 (注意:“让它尽可能紧凑”的原始问题实际上是这个问题的优化问题,并且仍然是NP-Hard)。
抱歉这个坏消息。
一些替代使用指数解决方案(例如backtracking),启发式或近似算法。
请注意,在一维空间中,问题有pseudo-polynomial solution,使用动态编程,但我不认为它可以简单地应用于二维空间(如果有的话)。
答案 1 :(得分:2)
正如amit所指出的,你的问题是NP难的。但是,这不应该阻止你简单地迭代砖块的所有排列,看看它们中哪一个最合适。也就是说,鉴于你没有“太多”砖块。
“太多”的价值主要取决于你的计算速度和你的可用时间,但我的猜测是,比如14的数值都可以。
如果蛮力解决方案太慢,您仍然可以尝试启发式算法或近似算法。
编辑:你的例子砖看起来非常人工。您的砖块尺寸是否符合某些标准,或者它们是完全随意的?也许你可以利用砖块大小的限制,如果有的话。