【问题标题】:Bin Packing Algorithm In Need of Speeding-Up需要加速的装箱算法
【发布时间】: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


【解决方案1】:

我不使用 Java,但由于过于复杂,您的实现似乎效率很低(正如您自己提到的)。算法本身也很奇怪,我没有尝试复制它,只是使用了明显的 O(bags^items) 蛮力算法,尝试将第一个项目放入每个袋子,对于每个案例都尝试放入第二个物品放入每个袋子等......

您可以将一个项目放入一个袋子中,而不是在堆栈上重复复制整个状态,探索具有此更改的树的分支,然后将项目从包

这是一个可以立即为您的 C# 测试用例完成的示例。

    static int[] itemSize;
    static int[] bagFreeSpace;
    static bool[,] doesBagContainItem; // in case this looks weird, [,] is a matrix, in java it would be [][]

    static bool pack(int item)
    { 
        // output the solution if we're done
        if (item == itemSize.Length)
        {
            for (int i = 0; i < bagFreeSpace.Length; i++)
            {
                Console.WriteLine("bag" + i);
                for (int j = 0; j < itemSize.Length; j++)
                    if (doesBagContainItem[i, j])
                        Console.Write("item" + j + "(" + itemSize[j] + ") ");
                Console.WriteLine();
            }
            return true;
        }

        // otherwise, keep traversing the state tree
        for (int i = 0; i < bagFreeSpace.Length; i++)
        {
            if (bagFreeSpace[i] >= itemSize[item])
            {
                doesBagContainItem[i,item] = true; // put item into bag
                bagFreeSpace[i] -= itemSize[item];
                if (pack(item + 1))                 // explore subtree
                    return true;
                bagFreeSpace[i] += itemSize[item];  // take item out of the bag
                doesBagContainItem[i,item] = false;
            }
        }

        return false;
    }

    static void Main(string[] args)
    {
        itemSize = new int[] { 6, 6, 6, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1 };
        bagFreeSpace = new int[] { 10, 10, 10, 10 };
        doesBagContainItem = new bool[bagFreeSpace.Length, itemSize.Length];

        if (!pack(0))
            Console.WriteLine("No solution");
    }

注意:如果你想并行执行,你需要给每个工人自己的状态副本(或每个作业 1 个副本),但只有在分支点,他们仍然可以像上面那样继续,而不需要复制国家。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 2013-02-03
    • 2011-12-31
    相关资源
    最近更新 更多