Как реализовать класс sparse_vector
Я реализую шаблонный класс sparse_vector. Это как вектор, но он хранит только те элементы, которые отличаются от созданного по умолчанию значения.
Таким образом, sparse_vector будет хранить лениво отсортированные пары индекс-значение для всех индексов, значение которых не равно T().
Я основываю свою реализацию на существующих разреженных векторах в числовых библиотеках - хотя моя будет обрабатывать и нечисловые типы T. я смотрел на boost::numeric::ublas::coordinate_vector
а также eigen::SparseVector
,
Оба магазина:
size_t* indices_; // a dynamic array
T* values_; // a dynamic array
int size_;
int capacity_;
Почему они просто не используют
vector<pair<size_t, T>> data_;
Мой главный вопрос: каковы плюсы и минусы обеих систем и что в итоге лучше?
Вектор пар управляет size_ и acity_ для вас и упрощает сопутствующие классы итераторов; у него также есть один блок памяти вместо двух, так что он влечет за собой половину перераспределений и может иметь лучшую локальность ссылок.
Другое решение может искать быстрее, поскольку строки кэша заполняются только индексными данными во время поиска. Могут также быть некоторые преимущества выравнивания, если T является 8-байтовым типом?
Мне кажется, что вектор пар - лучшее решение, но оба контейнера выбрали другое решение. Зачем?
2 ответа
По сути, кажется, что они заново изобрели колесо (так сказать).
Я бы лично рассмотрел 2 библиотеки для ваших нужд:
- Локи, для
Loki::AssocVector
-> интерфейс карты, реализованный надvector
(что вы хотите сделать) - Boost.Iterator, для его
iterator_adaptor
учебный класс. Упрощает реализацию нового контейнера с помощью Composition.
В качестве замечания я хотел бы отметить, что вы можете пожелать быть немного более общим, чем значения, отличные от T()
потому что это навязать T
быть DefaultConstructible. Вы могли бы предоставить конструктор, который принимает T const&
, При написании универсального контейнера полезно постараться максимально сократить необходимые требования (при условии, что это не влияет на производительность).
Кроме того, я хотел бы напомнить вам, что идея использования vector
для хранения очень хорошо для небольшого числа значений, но вы можете изменить базовый контейнер в сторону классического map
или же unordered_map
если количество значений растет. Это может стоить профилирования / времени. Обратите внимание, что STL предлагает эту возможность с контейнерными адаптерами, такими как stack
хотя это может сделать реализацию немного сложнее.
Повеселись.
Наличие индексов в отдельном списке ускорит их поиск - как вы предлагаете, он будет более эффективно использовать кэш, особенно если T велико.
Если вы хотите реализовать свой собственный, почему бы просто не использовать std::map
(или же std::unordered_map
)? Ключи были бы больше, но время реализации было бы близко к нулю!