Чтобы реализовать вариант 2, определите перечислимый тип для серотипов в другом классе, а затем напишите шаблонизированный экстрактор ключей. Я не уверен, что boost::multi_index_container
поддерживает переиндексацию (я сомневаюсь, что это так), поэтому этот подход может быть обречен с самого начала.
class ... {
enum Serotype {
...
sup // 1 greater than the highest serotype
};
...
};
template <class H, typename H::Serotype s>
struct serotype_key_extractor {
typedef bool result_type;
result_type operator()(const boost::shared_ptr<Host>& h) const
{ ! return h->isInfected(s); }
};
Конечно, для этого требуется индекс для каждого серотипа на вашем multi_index_container
(Serotype::sup
всего), с вероятной эффективностью O (I * lg (n)) для каждого индекса в течение всего времени моделирования (где I - количество событий заражения, а n - количество хостов). Если есть один или два общих запроса, вы можете использовать составные ключи , с одним из компонентов экстракторов ключей серотипа.
Вариант 4, отслеживающий зараженные хосты на карте наборов std::map<H::Serotype, std::set<boost::shared_ptr<Host> > >
, можно сделать более производительным по сравнению с событиями заражения за счет запроса. Вставка в / удаление из набора может быть O (lg (n)), поэтому может быть лучше использовать хеш или список, а не набор, хотя это увеличит стоимость запросов. Поскольку вы запускаете симуляцию, синхронизировать эту структуру не так уж и плохо. Обновите его во время заражения (или лечения, если они существуют). Если вы используете программирование на основе событий, это довольно просто.
Для запроса с опцией 4 вы можете построить другие наборы на основе других атрибутов, а затем найти пересечение этих наборов. Это O(f(I)+f(n)+g(S,T))
в целом, где f(x)
- это стоимость вставки x
элементов, g(X,Y)
- это стоимость пересекающихся множеств и S
и T
- размеры наборов (O(f(I)+f(N))
получается из построения всех наборов; я играю его немного незаметно с учетом времени, так как я можно предположить, что f (x) гомоморфно , что, скорее всего, не так). В лучшем случае, f(x)=x
и g(X,Y)=X+Y
, в зависимости от вашей установленной реализации. В качестве альтернативы, сбросьте по одному из наборов (например, найдите всех хозяев определенного возраста, затем подсчитайте число зараженных; count_if
- это конкретная складка, которую вы можете использовать, где count_if(start, end, pred)
эквивалентно foldl(start, end, [](int count, const boost::shared_ptr<Host>& h){return count + pred(h); })
). В последнем случае используйте самый ограничительный индекс, чтобы максимально сократить число подгрупп, которые необходимо сканировать. Это O (f (I) + S) в целом.
Часть трудностей при разработке эффективного решения заключается в том, что вы действительно можете использовать только один независимый индекс за раз. Внутри групп из индекса другие атрибуты, по которым вы можете индексировать, расположены в основном случайным образом. Если два индекса не являются независимыми (те, которые географически близки, с большей вероятностью могут иметь одинаковые инфекции), вы можете использовать оба одновременно, хотя это не дает вам большого успеха: вы можете получить большую часть суб -популяция в начале запроса, но вам все равно придется изучить остальную группу из первичного индекса.
Если вы не состоите в браке с C ++, вы можете переключиться на C # и обрабатывать запросы с помощью LINQ.
Что касается переключения на базу данных, как предполагает mathmike, вы, вероятно, захотите использовать только объектно-ориентированную базу данных в памяти. Кристофер Браун перечисляет некоторые, в том числе FastDB и OOFile , хотя я не могу сказать, насколько они подходят для вашего проекта.