Реализация бин упаковки в C ++ с STL - PullRequest
2 голосов
/ 10 июля 2010

Я впервые пользуюсь этим сайтом, поэтому извините за неправильное форматирование или странные формулировки, я постараюсь сделать все возможное, чтобы соответствовать правилам на этом сайте, но вначале я мог сделать некоторые ошибки.

Сейчас я работаю над реализацией некоторых различных алгоритмов упаковки бинов в C ++ с использованием контейнеров STL.В текущем коде у меня все еще есть некоторые логические ошибки, которые необходимо исправить, но этот вопрос больше касается структуры программы.Я не хотел бы получить второе мнение о том, как вы должны структурировать программу, чтобы минимизировать количество логических ошибок и сделать ее максимально удобной для чтения.В его текущем состоянии я просто чувствую, что это не лучший способ сделать это, но я действительно не вижу другого способа написать свой код прямо сейчас.

Проблема заключается в динамическом онлайнпроблема с упаковкойОн динамичен в том смысле, что у предметов есть произвольное время, прежде чем они покинут корзину, для которой они были назначены.

Короче говоря, мои вопросы:
Как изменится структура корзины?Алгоритм упаковки в 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 ]

2 голосов
/ 10 июля 2010

Как будет выглядеть структура алгоритма упаковки бина в C ++?

В идеале у вас должно быть несколько алгоритмов упаковки бинов, разделенных на разные функции, которые отличаются только логикой алгоритма. Этот алгоритм должен быть в значительной степени независимым от представления ваших данных, поэтому вы можете изменить свой алгоритм только одним вызовом функции.

Вы можете посмотреть, что общего у STL Algorithms . В основном, они работают на итераторах, а не на контейнерах, но, как я подробно опишу ниже, я бы не советовал это для вас изначально. Вы должны понять, какие алгоритмы доступны, и использовать их в своей реализации.

Являются ли контейнеры STL хорошим инструментом, чтобы реализация могла обрабатывать вводы произвольной длины?

Обычно это работает так: создайте контейнер, заполните контейнер, примените алгоритм к контейнеру.

Судя по описанию ваших требований, именно так вы и будете использовать это, поэтому я думаю, что все будет хорошо. Есть одно важное отличие между вашим алгоритмом упаковки бинов и большинством алгоритмов STL.

Алгоритмы STL либо не модифицируют, либо вставляют элементы в пункт назначения. bin-pack, с другой стороны, это «вот список корзин, используйте их или добавьте новую корзину». Это не невозможно сделать с итераторами, но, вероятно, не стоит усилий. Я бы начал работать с контейнером, получить работающую программу, создать ее резервную копию, а затем посмотреть, можно ли заставить ее работать только для итераторов.

Как мне обращаться с контейнерами в хорошем, удобном для чтения и реализации виде?

Я бы взял этот подход, охарактеризовал ваши входы и выходы:

  • Ввод: набор элементов, произвольная длина, произвольный порядок.
  • Вывод: Коллекция бинов определяется алгоритмом. Каждая корзина содержит коллекцию предметов.

Тогда я бы беспокоился о том, "что должен делать мой алгоритм?"

  • Постоянно проверяйте корзины на «подходит ли этот элемент?»
  • Ваш Class_bin - хорошая инкапсуляция того, что нужно.
  • Старайтесь не загромождать свой код несвязанными вещами, такими как «print ()» - используйте функции помощи, не связанные с членами.

type_item

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

Неясно, для чего используется life (или смерть). Я не могу представить, чтобы эта концепция имела отношение к реализации алгоритма бин-упаковки. Может быть, это следует исключить?

Это личное предпочтение, но я не люблю отдавать 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_ для всех ваших типов - он просто немного избыточен, и это должно быть ясно из кода. (Это вариант венгерской нотации . Программисты склонны к этому враждебны.)
  • У вас не должно быть члена класса 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 голос
/ 10 июля 2010

Некоторые мысли:

Ваши имена в некоторых местах перепутаны.

  1. У вас есть много параметров с именем input, это просто бессмысленно
  2. Я ожидаю, что full () проверит, заполнен ли он, а не может ли он соответствовать чему-то другому
  3. Я не думаю, что push_bin толкает корзину
  4. check_dead изменяет объект (я ожидаю, что что-то с именем check_ * просто расскажет мне что-нибудь об объекте)
  5. Не помещайте такие вещи, как Class и вводите имена классов и типов.
  6. class_list_of_bins, кажется, описывает то, что внутри, а не то, что объект.
  7. push_list не выдвигает список
  8. Не добавляйте такие вещи, как _list, к каждому методу в классе списка, если это объект списка, мы уже знаем его метод списка

Я запутался, учитывая параметры жизни и нагрузки относительно того, что вы делаете. У проблемы с упаковкой мусорного ведра, с которой я знаком, просто есть размеры. Я предполагаю, что со временем некоторые объекты вынимаются из мусорных ведер и, таким образом, уходят?

Некоторые дальнейшие мысли о ваших классах

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_required и является ли объект еще живым
  • Я ничего не включал в сортировку, потому что мне не совсем понятно, для чего это нужно в алгоритме первой подгонки.
0 голосов
/ 10 июля 2010

Это интервью дает отличное понимание обоснования STL.Это может дать вам некоторое представление о том, как реализовать ваши алгоритмы в стиле STL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...