使用STL在C ++中实现Bin打包实现

时间:2010-07-10 05:10:06

标签: c++ stl bin-packing

这是我第一次使用这个网站,对于任何糟糕的格式或奇怪的配方都很抱歉,我会尽力遵守本网站的规则,但我可能会在开始时做一些错误。

我现在正在使用STL容器在C ++中实现一些不同的bin打包算法。在当前的代码中,我仍然有一些需要修复的逻辑错误,但这个问题更多的是关于程序的结构。关于如何构建程序以尽量减少逻辑错误的数量并使其尽可能易于阅读,我不会对第二种意见发表意见。在它的当前状态中,我只是觉得这不是最好的方法,但我现在还没有看到任何其他方法来编写我的代码。

问题是动态在线装箱问题。它是动态的,因为物品在离开它们被分配到的箱子之前有任意时间。

总之我的问题是:
Bin打包算法的结构在C ++中如何看待? STL容器是一个很好的工具,使实现能够处理任意长度的输入吗? 我应该如何以良好,易于阅读和实施的方式处理容器?

关于我自己的代码的一些想法:
使用类来区分处理不同容器的列表和这些容器中的项目列表 尽可能有效地实施 易于运行,具有许多不同的数据长度和文件以进行基准测试。

#include <iostream>
#include <fstream>
#include <list>
#include <queue>
#include <string>
#include <vector>

using namespace std;

struct type_item {
    int size;
    int life;
    bool operator < (const type_item& input)
    {
        return size < input.size;
    }
};

class Class_bin {
    double load;
    list<type_item> contents;
    list<type_item>::iterator i;
public:
    Class_bin ();
    bool operator < (Class_bin);
    bool full (type_item);
    void push_bin (type_item);
    double check_load ();
    void check_dead ();
    void print_bin ();
};

Class_bin::Class_bin () {
    load=0.0;
}

bool Class_bin::operator < (Class_bin input){
    return load < input.load;
}

bool Class_bin::full (type_item input) {
    if (load+(1.0/(double) input.size)>1) {
        return false;
    }
    else {
        return true;
    }
}

void Class_bin::push_bin (type_item input) {
    int sum=0;

    contents.push_back(input);
    for (i=contents.begin(); i!=contents.end(); ++i) {
        sum+=i->size;
    }
    load+=1.0/(double) sum;
}

double Class_bin::check_load () {
    return load;
}

void Class_bin::check_dead () {
    for (i=contents.begin(); i!=contents.end(); ++i) {
        i->life--;
        if (i->life==0) {
            contents.erase(i);
        }
    }
}

void Class_bin::print_bin () {
    for (i=contents.begin (); i!=contents.end (); ++i) {
        cout << i->size << "  ";
    }
}


class Class_list_of_bins {
    list<Class_bin> list_of_bins;
    list<Class_bin>::iterator i;
public:

    void push_list (type_item);
    void sort_list ();
    void check_dead ();
    void print_list ();
private:
    Class_bin new_bin (type_item);
    bool comparator (type_item, type_item);
};

Class_bin Class_list_of_bins::new_bin (type_item input) {
    Class_bin temp;

    temp.push_bin (input);

    return temp;
}

void Class_list_of_bins::push_list (type_item input) {
    if (list_of_bins.empty ()) {
        list_of_bins.push_front (new_bin(input));
        return;
    }
    for (i=list_of_bins.begin (); i!=list_of_bins.end (); ++i) {
        if (!i->full (input)) {
            i->push_bin (input);
            return;
        }
    }
    list_of_bins.push_front (new_bin(input));
}

void Class_list_of_bins::sort_list () {
    list_of_bins.sort();
}

void Class_list_of_bins::check_dead () {
    for (i=list_of_bins.begin (); i !=list_of_bins.end (); ++i) {
        i->check_dead ();
    }
}

void Class_list_of_bins::print_list () {
    for (i=list_of_bins.begin (); i!=list_of_bins.end (); ++i) {
        i->print_bin ();
        cout << "\n";
    }
}


int main () {
    int i, number_of_items;

    type_item buffer;
    Class_list_of_bins bins;

    queue<type_item> input;

    string filename;
    fstream file;


    cout << "Input file name: ";
    cin >> filename;
    cout << endl;

    file.open (filename.c_str(), ios::in);

    file >> number_of_items;

    for (i=0; i<number_of_items; ++i) {
        file >> buffer.size;
        file >> buffer.life;
        input.push (buffer);
    }

    file.close ();

    while (!input.empty ()) {
        buffer=input.front ();
        input.pop ();
        bins.push_list (buffer);
    }

    bins.print_list ();

    return 0;
}

请注意,这只是我的代码的快照,但尚未正常运行

不要因为无关的喋喋不休而烦恼,只想感谢贡献的人,我会审查我的代码,希望能够更好地构建我的编程

3 个答案:

答案 0 :(得分:2)

  

Bin打包算法的结构如何在C ++中显示?

好吧,理想情况下,你会有几个bin-packing算法,分成不同的函数,这些算法的区别仅在于算法的逻辑。该算法应该在很大程度上独立于数据的表示,因此您只需使用一个函数调用即可更改算法。

您可以查看STL Algorithms的共同点。主要是,它们使用迭代器而不是容器,但正如我在下面详细介绍的那样,我最初不会建议你这样做。您应该了解可用的算法,并在实施中利用它们。

  

STL容器是否是使实现能够处理任意长度输入的好工具?

它的工作原理如下:创建一个容器,填充容器,将算法应用到容器中。

从您对要求的描述来判断,这就是您将如何使用它,所以我认为它会没事的。您的bin打包算法和大多数STL算法之间存在一个重要的区别。

STL算法要么是非修改的,要么是将元素插入目的地。另一方面,bin-packing是“这里是一个箱子列表,使用它们或添加一个新箱子”。用迭代器做这个并不是不可能的,但可能不值得努力。我首先操作容器,获取一个工作程序,备份它,然后看看你是否可以让它只用于迭代器。

  

我应该如何以良好,易读和实施的方式处理容器?

我采用这种方法,描述你的输入和输出:

  • 输入:项目集合,任意长度,任意顺序。
  • 输出:由算法确定的箱的集合。每个bin都包含一组项目。

然后我担心“我的算法需要做什么?”

  • 不断检查垃圾箱“这个项目适合吗?”
  • 您的Class_bin是对所需内容的良好封装。
  • 避免使用“print()”等不相关的内容使代码混乱 - 使用非成员帮助功能。
  

type_item

struct type_item {
    int size;
    int life;
    bool operator < (const type_item& input)
    {
        return size < input.size;
    }
};

目前还不清楚life(或死亡)的用途。我无法想象这个概念与实现bin-packing算法有关。也许它应该被遗漏?

这是个人偏好,但我不喜欢将operator<提供给我的对象。对象通常是非平凡的,具有很少的含义。例如,一种算法可能希望在死项之前排序所有活动项。为了清楚起见,我通常将其包装在另一个结构中:

struct type_item {
    int size;
    int life;
    struct SizeIsLess {
      // Note this becomes a function object, which makes it easy to use with
      // STL algorithms.
      bool operator() (const type_item& lhs, const type_item& rhs)
      {
          return lhs.size < rhs.size;
      }
    }
};

vector<type_item> items;
std::sort(items.begin, items.end(), type_item::SizeIsLess);
  

Class_bin

class Class_bin {
    double load;
    list<type_item> contents;
    list<type_item>::iterator i;
public:
    Class_bin ();
    bool operator < (Class_bin);
    bool full (type_item);
    void push_bin (type_item);
    double check_load ();
    void check_dead ();
    void print_bin ();
};
  • 我会跳过你所有类型的Class_前缀 - 它只是有点过分,应该从代码中清楚。 (这是hungarian notation的变体。程序员往往对它充满敌意。)
  • 你不应该有一个类成员i(迭代器)。它不是阶级国家的一部分。如果您需要所有成员,那没关系,只需在那里重新声明。如果输入时间过长,请使用typedef
  • 很难量化“bin1小于bin2”,所以我建议删除operator<
  • bool full(type_item)有点误导。我可能会使用bool can_hold(type_item)。对我来说,如果剩余空间,bool full()将返回true。
  • check_load()似乎更清楚地命名为load()
  • 同样,还不清楚check_dead()应该完成什么。
  • 我认为你可以删除print_bin并将其作为非会员功能写下来,以保持对象更清洁。
  • StackOverflow上的一些人会向我发射,但我会考虑将其作为一个结构,并将负载和项目列表公之于众。在这里看起来你并不关心封装(你只需要创建这个对象,这样你就不需要每次重新计算负载了。)
  

Class_list_of_bins

class Class_list_of_bins {
    list<Class_bin> list_of_bins;
    list<Class_bin>::iterator i;
public:

    void push_list (type_item);
    void sort_list ();
    void check_dead ();
    void print_list ();
private:
    Class_bin new_bin (type_item);
    bool comparator (type_item, type_item);
};
  • 我认为你完全没有这门课。
  • 从概念上讲,它代表一个容器,所以只需使用一个STL容器即可。您可以将这些方法实现为非成员函数。请注意,sort_list可以替换为std::sort
  • comparator过于通用了,它没有说明它的比较或原因,所以请考虑更清楚。
  

总体评论

总的来说,我认为你选择的课程充分模拟了你想要代表的空间,所以你会没事的。

我可能会像这样构建我的项目:

struct bin {
  double load;  // sum of item sizes.
  std::list<type_item> items;

  bin() : load(0) { }
};

// Returns true if the bin can fit the item passed to the constructor.
struct bin_can_fit {
  bin_can_fit(type_item &item) : item_(item) { }
  bool operator()(const bin &b) {
    return item_.size < b.free_space;
  }
 private:
  type_item item_;
};

// ItemIter is an iterator over the items.
// BinOutputIter is an output iterator we can use to put bins.
template <ItemIter, BinOutputIter>
void bin_pack_first_fit(ItemIter curr, ItemIter end, BinOutputIter output_bins) {
  std::vector<bin> bins;  // Create a local bin container, to simplify life.
  for (; curr != end; ++curr) {
    // Use a helper predicate to check whether the bin can fit this item.
    // This is untested, but just for an idea.
    std::vector<bin>::iterator bin_it =
        std::find_if(bins.begin(), bins.end(), bin_can_fit(*curr));
    if (bin_it == bins.end()) {
      // Did not find a bin with enough space, add a new bin.
      bins.push_back(bin);
      // push_back invalidates iterators, so reassign bin_it to the last item.
      bin_it = std::advance(bins.begin(), bins.size() - 1);
    }

    // bin_it now points to the bin to put the item in.
    bin_it->items.push_back(*curr);
    bin_it->load += curr.size();
  }
  std::copy(bins.begin(), bins.end(), output_bins);  // Apply our bins to the destination.
}

void main(int argc, char** argv) {
  std::vector<type_item> items;
  // ... fill items
  std::vector<bin> bins;
  bin_pack_first_fit(items.begin(), items.end(), std::back_inserter(bins));
}

答案 1 :(得分:1)

一些想法:

你的名字有些混乱。

  1. 你有很多名为输入的参数,那只是毫无意义的
  2. 我希望full()检查它是否已满,而不是它是否适合其他东西
  3. 我认为push_bin不推箱子
  4. check_dead修改对象(我希望命名为check_ *的东西,告诉我一些关于对象的东西)
  5. 不要在类和类型的名称中放置类和类型。
  6. class_list_of_bins似乎描述的是内部而不是对象是什么。
  7. push_list不会推送列表
  8. 不要将_list之类的东西附加到列表类中的每个方法,如果它是一个列表对象,我们已经知道它的列表方法
  9. 考虑到生活和负荷的参数,我对你正在做的事感到困惑。我熟悉的垃圾箱包装问题只是尺寸。我猜是加班时间有些物品是从箱子中取出来的,因此会消失?

    关于课程的一些进一步想法

    Class_list_of_bins向外界暴露了太多自己。为什么外界想要check_dead或sort_list?这是无关紧要的事情,但对象本身。你应该对那个类应该有的公共方法应该是这样的    *将一个项目添加到箱子集合中    *打印解决方案    *步骤一步到未来

    list<Class_bin>::iterator i;
    

    糟糕,糟糕,糟糕!除非它们实际上是成员状态,否则不要将成员变量放在您的上。您应该定义使用它的迭代器。如果你想保存一些输入,请添加:typedef list :: iterator bin_iterator,然后使用bin_iterator作为类型。

    扩展答案

    这是我的伪代码:

    class Item
    {
         Item(Istream & input)
         {
             read input description of item
         }
    
         double size_needed() { return actual size required (out of 1) for this item)
         bool alive() { return true if object is still alive}
         void do_timestep() { decrement life }
         void print() { print something }
    }
    
    class Bin
    {
        vector of Items
        double remaining_space
    
    
        bool can_add(Item item) { return true if we have enough space}
        void add(Item item) {add item to vector of items, update remaining space}
        void do_timestep() {call do_timestep() and all Items, remove all items which indicate they are dead, updating remaining_space as you go}
        void print { print all the contents }
    }
    
    class BinCollection
    {
       void do_timestep { call do_timestep on all of the bins }
       void add(item item) { find first bin for which can_add return true, then add it, create a new bin if neccessary }
       void print() { print all the bins }
    }
    

    一些快速说明:

    • 在你的代码中,你反复将int size转换为float,这不是一个好主意。在我的设计本地化到一个地方
    • 您会注意到与单个项目相关的逻辑现在包含在项目本身中。其他对象只能看到对它们很重要的是什么,size_required以及对象是否还活着
    • 我没有包含任何有关排序内容的内容,因为我不清楚首先适合算法的内容是什么。

答案 2 :(得分:0)

This interview可以深入了解STL背后的基本原理。这可能会为您提供一些如何以STL方式实现算法的灵感。