【发布时间】:2013-11-12 22:58:23
【问题描述】:
我正在寻找一种智能方法来解决常见的装箱问题。给定一些具有一定容量的袋子(我这样称呼它们),以及占用一定空间的物品列表,任务是确定是否所有物品都可以放入袋子中;如果是这样,如何。我现在有一个详尽的 DFS 工作,但它需要......永远。我的 DFS 是迭代的,需要在每一步复制整个状态,这非常昂贵。这是我针对 4 个容量为 10 的袋子的特定问题的代码(如果您不想全部查看,此代码的真正相关部分只是 pack() 方法和 State 类):
import java.util.ArrayList;
import java.util.Stack;
public class BagProblem {
int numBags;
int bagCapacity;
ArrayList<Item> items = new ArrayList<Item>();
public static void main(String[] args) {
BagProblem bp = new BagProblem(4, 10);
bp.pack();
}
public BagProblem(int numBags, int bagCapacity) {
this.numBags = numBags;
this.bagCapacity = bagCapacity;
items = new ArrayList<Item>();
items.add(new Item("item0", 6));
items.add(new Item("item1", 6));
items.add(new Item("item2", 6));
items.add(new Item("item5", 3));
items.add(new Item("item6", 3));
items.add(new Item("item7", 3));
items.add(new Item("item8", 2));
items.add(new Item("item9", 2));
items.add(new Item("item10", 2));
items.add(new Item("item11", 2));
items.add(new Item("item12", 2));
items.add(new Item("item13", 2));
items.add(new Item("item14", 1));
}
// find a valid way to pack and print the items in each Bag, or
// print failure
public void pack() {
Stack <State> s = new Stack<State>();
Bag[] currBags = new Bag[numBags];
for (int i = 0; i < numBags; i++) {
currBags[i] = new Bag(bagCapacity);
}
s.push(new State(currBags));
while(!s.isEmpty()) {
State currState = s.pop();
for (Item i : items) {
if (!currState.containsItem(i)) {
State newState = new State(currState.bags);
newState.numItems = currState.numItems;
if (newState.addItem(i)) {
s.push(newState);
if (newState.numItems == items.size()) {
System.out.println("success");
System.out.println(newState);
return;
}
}
}
}
}
System.out.println("failure");
}
private class State {
Bag[] bags;
int numItems;
public State(Bag[] currBags) {
bags = new Bag[numBags];
for (int i = 0; i < numBags; i++) {
bags[i] = new Bag(bagCapacity);
}
// figure out how to actually copy this
for (int j = 0; j < numBags; j++) {
Bag bagToCopy = currBags[j];
for (Item item : bagToCopy.contents) {
Item newItem = new Item(item.name, item.size);
bags[j].size = bagToCopy.size;
bags[j].contents.add(newItem);
}
}
}
public boolean addItem(Item i) {
for (Bag b : bags) {
if (b.addItem(i)) {
numItems++;
return true;
}
}
return false;
}
public boolean containsItem(Item i) {
for (Bag b : bags) {
for (Item item : b.contents) {
if (item.name.equals(i.name))
return true;
}
}
return false;
}
public String toString() {
String output = "";
for (Bag b : bags) {
for (Item j : b.contents) {
output += j.name + " ";
}
output += "\n";
}
return output;
}
}
private class Bag {
int capacity;
int size;
ArrayList<Item> contents;
public Bag(int capacity) {
this.capacity = capacity;
this.size = 0;
contents = new ArrayList<Item>();
}
public boolean addItem(Item i) {
if(size + i.size > capacity)
return false;
contents.add(i);
size += i.size;
return true;
}
public String toString() {
String output = "";
for (Item i : contents) {
output += i.name + " ";
}
return output + "\n";
}
}
private class Item {
String name;
int size;
public Item(String name, int size) {
this.name = name;
this.size = size;
}
public String toString() {
return name;
}
}
}
大约一百万年后,它确实会给出一个正确的答案(如果你尝试运行它,你可能不想真正等待那么久):
success
item14 item7 item6 item5
item13 item12 item2
item11 item10 item1
item9 item8 item0
每行表示一个单独的包。我怎样才能加快速度?我知道尝试先放置最大的项目等有启发式方法,但我真正感兴趣的是获得基本的 DFS(或者我应该尝试回溯?)以减少开销;以后我会努力变得更好。
任何帮助将不胜感激。
【问题讨论】:
-
您好,欢迎来到 SO。您是否尝试过在 Google 和 Wikipedia 上查找算法?例如,first-fit-algoirthm 可以非常快地产生答案,但它不会是最优的
-
问题是这个装箱问题有点扭曲。我需要知道给定数量的具有一定容量的袋子是否可以包含一组特定大小的物品; first-fit 和其他非最优算法将无法分辨。我认为 DFS 或回溯必须是唯一的方法,但它太慢了。当然,首先尝试的物品和包的不同安排可能会有所帮助,但现在我担心我在每一步复制的信息比我真正需要的更多,这会缩短我的运行时间。
-
有什么变化?您正在准确描述标准的装箱问题。这个问题是 NP 难的,因此任何保证找到最佳答案的解决方案在大型实例上都会很慢。但是如果 first-fit 或不同的启发式将项目打包到 k 个包中,您知道最佳答案最多为 k,并且您可能能够证明它不能是 k-1 或更少(例如,如果总大小的项目超过 (k-1)*bag_size),在这种情况下,您将知道答案是最佳的。
-
嗯,我通常看到的问题被表述为“这需要多少个箱子?”,这给了您在最优性方面的一些回旋余地,而不是“这个数量的箱子可以工作吗?”我想这并不是一个真正的转折,但我认为没有任何捷径可以通过首次拟合或其他算法可靠地得到正确答案。
标签: java algorithm depth-first-search backtracking