vector, list или multimap. Помогите, пожалуйста, выбрать класс

Тема в разделе "WASM.BEGINNERS", создана пользователем Pahan, 13 дек 2009.

  1. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    Здравствуйте.
    В программе есть две структуры данных, которые вызывают вопросы:
    1) Массив Temp, где каждый элемент типа
    Код (Text):
    1.   struct subseq {
    2.     string str;
    3.     char checked;
    4.    };
    На каждой итерации цикла, он обрабатывается по следующей схеме:

    DO <условие>
    - создание заново путем добавления элементов в конец
    - последовательный просмотр с изменением поля checked
    - удаление всех элементов
    OD
    Суммарно на всех итерациях требуется вставка порядка 2*10^6 элементов. Соотвественно столько же удалений.

    2) Массив Base. Представляет собой справочную таблицу, где каждый элемент типа:

    Код (Text):
    1.    struct adrbas {
    2.     string str;
    3.     vector<int> address;
    4.     char exs;
    5.     short int cnt;
    6.    };
    Обрабатывается на каждой итерации данная таблица аналогичным образом:
    - создается путем добавления элементов в конец. При этом поле str здесь является ключом, на
    основе которого решается добавить ли новый элемент или, если элемент с таким ключом str существует, то в найденную строку просто вносятся необходимые изменения
    - последовательно просматривается
    - часть записей таблицы удаляются

    Суммарно на всех итерациях количество вставок/удалений тоже достаточно большое.


    Помогите, пожалуйста, советом по выбору стандартного класса для реализации задачи.
    Что лучше выбрать: vector, list или м.б. multimap. Объемы вычислений большие и время работы очень критично.
    Спасибо.
     
  2. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    если есть ключ, то надо либо map, либо multimap. можно еще использовать set, но тогда надо будет писать процедуру сравнения элементов (сравнение будет только по полю str).

    я считаю, что в твоем случае лучше всего будет использовать map<string,adrbas>, где из adrbas следует убрать поле str.
    правда, при последовательном просмотре контейнера элементы будут отсортированы по ключу. если тебе важен ПОРЯДОК, в котором элементы были добавлены, то я не знаю пока что посоветовать.

    не знаю, с чего ты вдруг решил использовать multimap. это тот же map, только допускает наличие элементов с одинаковыми ключами.

    P.S. хочется высказаться еще насчет vector и list (касаемо не этой задачи, а вообще). vector хорош лишь тем, что обеспечивает быстрый произвольный доступ к элементу типа vec[0] или vec[5]. но вставка в середину очевидно медленная, да и после такой операции все итераторы становятся непригодными :) поэтому я для себя давно заметил, что в большинстве задач лучше списки, чем динамические массивы.
     
  3. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    ...и если не секрет, это в какой такой дзенской задаче понадобилось 2*10^6 элементов? =)
     
  4. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    2luckysundog
    Спасибо за мысли. Задача если упрощенно, то такая:
    есть входная строчка из символов какого-то конечного алфавита. Необходимо всю ее разобрать и составить что-то вроде словаря со след. инфой: все строчки длины 1 и сколько раз встречается каждая, все строчки длины 2 и сколько раз встречается каждая и т.д. Например:
    Исходная: 12120, должно получится:
    substr len cnt
    "1" 1 2
    "2" 1 2
    "0" 1 1
    "12" 2 2
    "21" 2 1
    "20" 2 1
    ...

    и т.д. Плюс еще какая-нибудь доп. инфа - например позиции символов в строке, с которых начинается данная подстрока. Вот и получается, что если строка длины 2000 хотя-бы, то всего так или иначе нужно рассмотреть и вставить.
    2000+...+1 подстрок. (я считал худший случай - когда они все неповторяющиеся)

    Соответсвенно нужен постоянный поиск по уже внесенным и корректировка значений - если рассматриваемая подстрока уже внесена в базу.
    Скажите, теперь с этой информацией, совет - что лучше использовать map - остался таким же? Если я правильно понял - то map - это НЕ списочная структура. Почему здесь использовать list не лучше? ведь необходимо большое количество вставок и удалений, которое с list-ом работает быстрее.

    P.S. Первоначально я все уже реализовал с vector-ом - но работает ооочень медленно. Поэтому, оптимизируя алгоритм, я решил более тщательно отнестись к выбору используемого класса.
     
  5. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    причем в дальнейшем при работе со справочной таблицей, мне нужно будет извлекать строки только определенной длины, допустим длины 4. Насколько я понимаю - при использовании map -это будет не совсем удобно.
     
  6. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    Pahan
    вставки и удаление - да, в list быстрее. но поиск, я так понимаю, у тебя сейчас линейный, а это жуть для таких объемов данных. в map поиск элемента с нужным ключом работает куда быстрее - это функция find(). не нужно обходить весь контейнер от начала до конца. такой полный обход нужен только когда нужно вывести все содержимое контейнера, например.
    для вставки - insert(). причем, если при вставке правильно использовать параметр position, то можно добиться довольно-таки быстрой вставки.
     
  7. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    можно задать свою функцию сравнения двух ключей. т.е. чтобы они располагались не просто в лексикографическом порядке, но и в порядке возрастания длин строк. тогда можно будет очень быстро найти в контейнере первую строку с длиной N и дальше извлечь все остальные строки (lower_bound() / upper_bound())
     
  8. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    т.е. например для длины N=4 выполняем lower_bound с ключом "aaaa" :)
    (если 'a' - самый первый символ алфавита)
     
  9. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    Кое-что ясно. Да, поиск сейчас линейный, но моя база "перестраивется" на ходу и в конкретный момент времени там находятся только подстроки необходимые для вычислений и определенной длины (m и m-1, m увеличивается), а остальная (предыдущая) часть уже не нужна "отсекается". Т.е максимально там единовременно находится 2000+1999 подстрок длины 1 и 2. Соответственно "дороже" всего стоят операции вставок и удалений - а значит похоже лучше списки. Так? Хотя, конечно же можно и подумать о том, чтобы не "усекать" базу, а просто всегда пользоваться find-ом в этом огромном скоплении данных. Но наверное неизвестно окупится ли=)
    Я подумаю над этим. Подскажите, еще плиз - как мне прикрутить эту функцию сравнения less , чтобы компилятор использовал ее для сравнения. Просто написать:
    Код (Text):
    1. map(string, adrbas, less<string>)
    и ниже описать bool-евскую функцию less, которая вернет 0 или 1 (если первый параметр должен идти раньше второго)??

    Я вопросы жирным выделил, а то все вперемешку идет.
     
  10. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    Код (Text):
    1. #include <cstdio>
    2. #include <map>
    3. #include <string>
    4. #include <vector>
    5.  
    6. using namespace std;
    7.  
    8. typedef struct {
    9.     vector<int> address;
    10.     char exs;
    11.     short int cnt;
    12. } t_adrbas;
    13.  
    14. struct cmycomp {
    15.     bool operator() (const string& L, const string& R) const
    16.     {
    17.         if(L.length() == R.length()) return (L < R);
    18.         else return (L.length() < R.length());
    19.     }
    20. };
    21.  
    22. typedef map<string,t_adrbas,cmycomp> t_dic;
    23. typedef t_dic::iterator it_dic;
    24. typedef t_dic::value_type vt_dic;
    25.  
    26. int main()
    27. {
    28.     t_dic dic;
    29.     it_dic idic;
    30.     string sample = "12120";
    31.  
    32.     int i,n,ns = sample.length();
    33.     for(n=1;n<=ns;n++)
    34.     {
    35.         for(i=0;i<ns-n+1;i++)
    36.         {
    37.             string subs = sample.substr(i,n);
    38.             idic = dic.find(subs);
    39.             if(idic != dic.end())
    40.             {
    41.                 idic->second.address.push_back(i);
    42.                 idic->second.cnt ++;
    43.             }
    44.             else
    45.             {
    46.                 t_adrbas el;
    47.                 el.address.push_back(i);
    48.                 el.cnt = 1;
    49.                 dic.insert(vt_dic(subs,el));
    50.             }
    51.         }
    52.     }
    53.     for(idic=dic.begin();idic!=dic.end();idic++)
    54.     {
    55.         printf("\"%s\" len=%d cnt=%d\n",
    56.                 idic->first.c_str(),
    57.                 idic->first.length(),
    58.                 idic->second.cnt);
    59.     }
    60.     return 0;
    61. }
     
  11. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    если что, я не знаю как быстро работает функция length(), т.е. не знаю считает ли она каждый раз заново strlen() :) в этом случае лучше делать свой класс для строки или наследовать от string.
     
  12. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    За код спасибо большое. Пока искал описание map - понятнее примера не видел=)
    Если честно то не совсем понял про length(). А разве можно иначе? Помоему она всякий раз когда при сравнении будет вычислять заново.
    Если можно, то напишите подробнее про тот как сделать иначе.
     
  13. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    Pahan
    иначе, например, в Delphi. там строке сопутствует поле c длиной, которое обновляется после каких-либо операций со строкой. если добавляем N символов, то к длине прибавляется N и т.д. и функция Length() просто возвращает значение этого поля :)
    в данной задаче, если наследовать от класса string, можно перегрузить всего лишь operator=() (3 его варианта для string, char* и char), потому что никаких действий над строками не происходит кроме присваивания. ну и еще перегрузить саму функцию length().
     
  14. luckysundog

    luckysundog New Member

    Публикаций:
    0
    Регистрация:
    28 окт 2008
    Сообщения:
    106
    вообще, предложенный мной словарь далек от идеала :)
    советую посмотреть (причем внимательно) на алгоритм Ахо-Корасик для построения словаря. и на суффиксные деревья. к сожалению, сейчас не могу сказать насколько они уместны в этой задаче, но если выбирать из vector, list и map, то лучше на мой взгляд map
     
  15. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    Ок, общую идею я понял. Над деталями сам подумаю. Спасибо за помощь.
     
  16. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    В смысле общую идею про length(), а не Ахо-Корасика=)
    Ахо-Корасика посмотрю конечно.
     
  17. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    Поправлюсь. Ахо-Корасик не склоняется.