Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Сентябрь 16, 2018, 11:34



Название: Простой (?) поиск
Отправлено: Igors от Сентябрь 16, 2018, 11:34
Добрый день

Есть контейнер объектов, каждый имеет такие (интересующие нас) поля. все они НЕ уникальны

- имя (QString)
- тип (enum или int)
- visibility (bool)

Нужно: для заданного имени + типа найти "наилучший" объект в контейнере, т.е.

- имя должно точно совпадать, иначе объект не найден
- тип:  если совпадает с заданным - хорошо, это "наилучший", иначе годится любой
- visibility:  если true это "наилучший", иначе годится и false

И наконец если все ключи совпадают, должен быть найден первый (по порядку в исходном контейнеру). Как лучше/техничнее сделать такой поиск ?

Спасибо


Название: Re: Простой (?) поиск
Отправлено: deMax от Сентябрь 18, 2018, 17:18
Обьектов много? Имен не уникальных много? Если в лоб, находим первый объект с именем(если все совпало - в ответ), иначе ищем следующий (если лучше - заменяемб совпало в ответ)... и так пока все объекты не пройдем.

Чтобы быстрее искалось - QMap, QMsql.... Если объектов с одинаковым именем очень много, тогда БД - ищем полное совпадение, потом хуже и хуже...


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 18, 2018, 19:04
QMultiMap можно заюзать, если есть совпадающие имена. Тогда в значениях ее можно хранить структуру int/bool.


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 19, 2018, 08:08
Обьектов много?
...
 Если объектов с одинаковым именем очень много...
Причем тут много-мало? :) Интересует корректное, "академическое" решение задачи, действительно ли оно такое уж сложное? А может оно проще "прямого перебора" который кстати здесь достаточно хлопотливый ?

Чтобы быстрее искалось - QMap,
QMultiMap можно заюзать, если есть совпадающие имена. Тогда в значениях ее можно хранить структуру int/bool.
Да, можем создавать любые контейнеры (поиск нужен многократно). И что? Как искать-то?

Вначале я подумал слить все проверки в ключ (или оператор <) и отсортировать по этому (композитному) ключу. Тогда поиск: lower_bound и проверяем найденный + предыдуший. Но почему-то мне показалось это не проходит... Или я неправ?


Название: Re: Простой (?) поиск
Отправлено: Figaro от Сентябрь 19, 2018, 14:09
QMultiMap можно заюзать, если есть совпадающие имена. Тогда в значениях ее можно хранить структуру int/bool.

Согласен  с  QMultiMap, только в значениях проще хранить QVariant… Это мне кажется проще :)


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 19, 2018, 19:02
Обьектов много?
...
 Если объектов с одинаковым именем очень много...
Причем тут много-мало? :) Интересует корректное, "академическое" решение задачи, действительно ли оно такое уж сложное? А может оно проще "прямого перебора" который кстати здесь достаточно хлопотливый ?

Чтобы быстрее искалось - QMap,
QMultiMap можно заюзать, если есть совпадающие имена. Тогда в значениях ее можно хранить структуру int/bool.
Да, можем создавать любые контейнеры (поиск нужен многократно). И что? Как искать-то?

Вначале я подумал слить все проверки в ключ (или оператор <) и отсортировать по этому (композитному) ключу. Тогда поиск: lower_bound и проверяем найденный + предыдуший. Но почему-то мне показалось это не проходит... Или я неправ?
Исходя из первого поста если я правильно понял, то иерархию поиска можно описать по убыванию:
- имя
- видимость
- тип
т.о. у мапы ключ - имя, в значениях отсортированный лист структур, оператор < можно сделать только по полю "видимость".
Ну а далее либо самый первый в листе берем, либо есть видимость в фолсе, тогда переходим к следующему.


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 20, 2018, 08:27
Исходя из первого поста если я правильно понял, то иерархию поиска можно описать по убыванию:
- имя
- видимость
- тип
Нет, см первый пост: имя + тип + видимость
т.о. у мапы ключ - имя,
Чего это ??? Почему не
Код
C++ (Qt)
typedef QPair<QString, int> TKey;  // имя + тип
typedef QPair<QString, QPair<int, bool> > TKey;  // имя + тип + видимочть


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 20, 2018, 09:36
Я может не сильно до конца задачу понял, но показалось, что первичное условие это имя, и если его нет, то далее искать бессмысленно.
Поэтому поиск по оставшимся 2 параметрам можно сократить, объединив элементы с одинаковыми именами.


Название: Re: Простой (?) поиск
Отправлено: deMax от Сентябрь 20, 2018, 09:47
Причем тут много-мало? :) Интересует корректное, "академическое" решение задачи, действительно ли оно такое уж сложное? А может оно проще "прямого перебора" который кстати здесь достаточно хлопотливый ?
А что сложного в прямом переборе? Создаете пустой указатель и перебираете массив, заменяя значение более выгодным (если все совпало, поиск останавливаете).
Если память не так важна, и вызовов очень много, то загоните в QMap<QString /*Name*/, QMap<int /*Type*/, bool /*Visible*/>> и двумя строчками извлеките ответ.


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 20, 2018, 09:58
Я может не сильно до конца задачу понял, но показалось, что первичное условие это имя, и если его нет, то далее искать бессмысленно.
Да, так и есть, правильно поняли

Поэтому поиск по оставшимся 2 параметрам можно сократить, объединив элементы с одинаковыми именами.
??? Совершенно непонятный вывод. А остальные правила (или критерии) поиска разве нельзя задать в ключах мапы?

А что сложного в прямом переборе?
Ну вообще-то прямой перебор - "визитная карточка лоха"  :)

Если память не так важна, и вызовов очень много, то загоните в QMap<QString /*Name*/, QMap<int /*Type*/, bool /*Visible*/>> и двумя строчками извлеките ответ.
Какой Вы быстрый  :) Ну во-первых мапа не гарантирует что эл-ты с одним ключом будут в порядке исходного массива. И хотелось бы увидеть эти "две строчки извлечения"  :)


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 20, 2018, 13:16
есть мысль построить индексы по каждому полю, в значениях хранить лист индексов элементов в исходном массиве.
тогда при поиске делать пересечение всех трёх индексов.
С телефона код не напишу, могу вечером набросать.


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 20, 2018, 17:08
есть мысль построить индексы по каждому полю, в значениях хранить лист индексов элементов в исходном массиве.
тогда при поиске делать пересечение всех трёх индексов.
С телефона код не напишу, могу вечером набросать.
Спасибо, но давайте лучше уясним что писать-то, тогда собсно написание не проблема. Пусть напр ключи в мапе легли так
Цитировать
{ "Name", 1, false, 100 }  // имя, тип, видимость, индекс в исходном
{ "Name", 3, false, 10 }
{ "Name", 4, true, 101 }
Ищем { "Name", 2, false, 0 }. QMap::lowerBound (std::lowerBound) вернет итератор на второй { "Name", 3, false, 10 }, он "не меньше". Проверяем найденный и предыдущий - у обоих тип не совпадает, у обоих видимость false. Однако был ключ "лучше" - у последнего тоже тип не совпадает, зато видимость true. Поэтому я и решил что не проходит, нужно топать по всем эл-там пока искомое имя не кончится и руками искать "лучший". Верно ли я мыслюсь?


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 20, 2018, 19:32
вот чего накидал:

Код:
#include <QHash>
#include <QVector>
#include <QSet>
#include <QDebug>

#include <algorithm>

struct ObjectInfo
{
    QString name;
    int type;
    bool visible;

};

QList<ObjectInfo> objects;

QHash<QString, QList<int>> nameIndex;
QHash<int, QList<int>> typeIndex;
QHash<bool, QList<int>> visibleIndex;


void fill()
{
    for (int i = 0; i < 100; ++i)
    {
        const QString name = QString("name%1").arg(qrand()%5);
        const int type = qrand()%27;
        const bool visible = (i%2);
        ObjectInfo objectInfo{name, type, visible};

        const int index = objects.size();

        nameIndex[name] << index;
        typeIndex[type] << index;
        visibleIndex[visible] << index;
        objects << objectInfo;
    }
}

int find(const QString& name, const int type, const bool visible)
{
    auto it = nameIndex.find(name);
    if (it == nameIndex.cend()) // имя не найдено - увы
        return -1;

    if (it.value().isEmpty()) // ничего не проиндексировано - увы
        return -1;

    QSet<int> types = typeIndex.value(type).toSet();
    QSet<int> visibles = visibleIndex.value(visible).toSet();

    QSet<int> intersects = types.intersect(visibles); // пересекающиеся индексы типа и видимости
    if (intersects.isEmpty()) // ничего не персекается - возвращаем первый индекс по имени
        return it.value().first();

    intersects = intersects.intersect(it.value().toSet()); //пересечение имени и типа/видимости
    if (intersects.isEmpty())
        return it.value().first();

    QList<int> indexes = intersects.toList();
    auto min = std::min_element(indexes.cbegin(), indexes.cend());
    return *min;
}


int main(int argc, char *argv[])
{
    Q_UNUSED(argc);
    Q_UNUSED(argv);

    fill();

    const int index = find("name1", 5, true);
    if (index >= 0)
        qDebug() << objects.at(index).name;

    return 0;
}


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 21, 2018, 09:12
вот чего накидал:
Ну все-таки надо думать о производительности и расходе ресурсов (хотя бы иногда  :)). Создавать многочисленные контейнеры на каждом поиске - ну это уж слишком


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 21, 2018, 09:44
вот чего накидал:
Ну все-таки надо думать о производительности и расходе ресурсов (хотя бы иногда  :)). Создавать многочисленные контейнеры на каждом поиске - ну это уж слишком
Если объектов десятки-сотни миллионов, тогда поиск лучше возложить на БД.
Что касается производительности, то поиск по хешу О(1) в среднем. Пересечение только замедлить может.
Ну в общем, принцип пересечения подмножеств я предложил, дальше вам решать, подойдёт ли он под конкретную задачу.
ps: у меня в проекте с 300-2000 тыс объектов подобные индексации используются, производительности хватает, памяти на индексирование до 200мб жрется.


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 21, 2018, 09:56
Если объектов десятки-сотни миллионов, тогда ...
Ну вот, опять пошли "анализы" :) А можно просто "решить задачу" без всяких "если"? Неужели она и вправду настолько сложна, что надо ловчить, привлекать БД и.т.п ? Не аерю. По меньшей мере один вариант есть - в мапе пройтись по всем объектам с одинаковым именем (кода не больше)


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 21, 2018, 10:11
По меньшей мере один вариант есть - в мапе пройтись по всем объектам с одинаковым именем (кода не больше)
И смысл мапы при условии, что у всех объектов будет одинаковое имя?
ps: это частный случай, того что я написал.


Название: Re: Простой (?) поиск
Отправлено: ViTech от Сентябрь 21, 2018, 11:52
Ну вот, опять пошли "анализы" :) А можно просто "решить задачу" без всяких "если"? Неужели она и вправду настолько сложна, что надо ловчить, привлекать БД и.т.п ? Не аерю. По меньшей мере один вариант есть - в мапе пройтись по всем объектам с одинаковым именем (кода не больше)

Нельзя :). Т.е. можно, но это решение не будет одинаково эффективным для множества разных "если". Если элементов около сотни. Если элементов сотни миллионов. Если можно/нельзя использовать многопоточность. Если быстродействие важнее расходов по памяти. Если наоборот. Напишите одно решение, которое будет одинаково эффективно работать хотя бы для всех тех "если", которые я перечислил :).


Название: Re: Простой (?) поиск
Отправлено: SimpleSunny от Сентябрь 21, 2018, 13:29
Так а чем вариант с QMap <QMap... не подошел? Поиск правда не совсем 2мя строками, но...

Код
C++ (Qt)
QMap <QString, QMap <int, QMap <bool, Data*>>> byName;
QMap <QString, Data*> anyType;
 
Data* findObject(QString name, int type, bool visible)
{
   auto byType = byName.find(name);
   //has name
   if (byType != byName.end())
   {
       auto byVisibility = byType->find(type);
       //has type
       if (byVisibility != byType->end())
       {
           auto object = byVisibility->find(visibility);
           //has visible
           if (object != byVisibility->end())
               return *object;
           //no visible
           else
               return byVisibility[!visibility];
       }
       //no type
       else
       {
           return anyType[name];
       }
   }
 
   //no name
   return nullptr;
}

И заполнять обратным перебором как-то так
Код
C++ (Qt)
void insert(QString name, int type, bool visible, Data *value)
{
   anyType[name] = value;
   byName[name][type][visible] = value;
}


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 21, 2018, 13:34
Так а чем вариант с QMap <QMap... не подошел?
Не выполнится условие "первый в оригинальном массиве"


Название: Re: Простой (?) поиск
Отправлено: Racheengel от Сентябрь 21, 2018, 14:24
1. Проиндексировать все объекты по именам:
QMap<QString, QList<int> >

где в список для каждого уникального имени заносить порядковый номер объекта в оригинальном массиве.

2. При поиске, если имя в мапе найдено, то взять массив индексов и далее "в лоб" только по нему.
Ну а иначе объект не найден.

Вроде несложно, не? :)


Название: Re: Простой (?) поиск
Отправлено: SimpleSunny от Сентябрь 21, 2018, 19:26
Так а чем вариант с QMap <QMap... не подошел?
Не выполнится условие "первый в оригинальном массиве"

Почему это?


Название: Re: Простой (?) поиск
Отправлено: RedDog от Сентябрь 21, 2018, 19:56
Почему это?
Потому что во второй мапе ключ это тип, а не индекс в оригинальном массиве


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 22, 2018, 07:37
Напишите одно решение, которое будет одинаково эффективно работать хотя бы для всех тех "если", которые я перечислил :).
Да не вопрос
Код
C++ (Qt)
struct Node {
QString GetName( void ) const;
int GetType( void ) const;
bool GetVisibility( void ) const;
};
 
typedef QPair<QString, int> TKey; // name + index
typedef QPair<int, int> TAux; // id (as bool) + visible  (as bool)
typedef QMap<TKey, Node *> TFindMap;
 
TFindMap BuildFindMap( const QVector<Node *> & nodes )
{
TFindMap fmap;
for (int i = 0; i < nodes.size(); ++i)
fmap[TKey(nodes[i]->GetName(), i)] = nodes[i];
 
return fmap;
}
 
Node * FindNode( const TFindMap & fmap, const QString & name, int type )
{
TKey key(name, -1);
TFindMap::const_iterator it = fmap.lowerBound(key);
Node * bestNode = 0;
TAux bestAux(-1, -1);
while (it != fmap.end()) {
if (it.key().first != name) break;
Node * node = it.value();
TAux curAux((node->GetType() == type) ? 1 : 0,
node->GetVisibility() ? 1 : 0);
if (curAux > bestAux) {
bestNode = node;
bestAux = curAux;
if (bestAux.first && bestAux.second) break;
}
++it;
}
return bestNode;
}
 
Как это улучшить? И что там за дичь с эл-тами которые сами контейнеры? Может лучше на чистом "С" потренироваться,  чтобы контейнерами так не ляпать?

Нельзя :). Т.е. можно, но это решение не будет одинаково эффективным для множества разных "если". Если элементов около сотни. Если элементов сотни миллионов. Если можно/нельзя использовать многопоточность. Если быстродействие важнее расходов по памяти. Если наоборот.
Ну конечно играть роль "эксперта" приятно  :), но в очередной раз замечаю (и/или замечу): углубление в детали проекта часто/обычно работает в минус. Проект живет своей жизнью и выводы могут быстро оказаться устаревшими. Напр пока я помню проект с макс 50К объектами, но в связи с недавним переводом на 64 запросто может быть уже и 200-300К. Также оценить "кратность" непросто даже в среднем проекте. Да, объектов может быть и сотка, но  поиск зовется для каждой ячейки в таблице. Только убедиться в этом - неск дней разрывания кода, часто чужого. Так может за это время просто сделать "нормально"?  :)


Название: Re: Простой (?) поиск
Отправлено: SimpleSunny от Сентябрь 22, 2018, 12:11
Почему это?
Потому что во второй мапе ключ это тип, а не индекс в оригинальном массиве

Для каждой комбинации (имя,тип,видимость) хранится только лучшее соответствие, поэтому все подходит под заданные условия.


Название: Re: Простой (?) поиск
Отправлено: ViTech от Сентябрь 22, 2018, 13:45
Напишите одно решение, которое будет одинаково эффективно работать хотя бы для всех тех "если", которые я перечислил :).
Да не вопрос
Код
C++ (Qt)
...
 

Ещё какой вопрос. Вот сидит пользователь, постоянно изменяет данные в контейнере с парой-тройкой сотен тысяч элементов. Иногда(очень редко) ищет  элемент по заданным условиям, смотрит на него, и продолжает дальше изменять данные. В предложенном решении каждый раз формируется дополнительный TFindMap (+ память, + время), хотя в лучшем случае нужный элемент найдётся в исходном контейнере менее чем за один проход, в худшем - один проход и ещё немного по наиболее подходящим элементам. И где в Вашем решении хоть какая-нибудь эффективность?  На кой нужен дополнительный TFindMap? Оно не эффективно ни по одному критерию из тех, что я приводил. Это плохое решение, переделывайте :).


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 22, 2018, 17:28
[Ещё какой вопрос. Вот сидит пользователь, постоянно изменяет данные в контейнере с парой-тройкой сотен тысяч элементов. Иногда(очень редко) ищет  элемент по заданным условиям, смотрит на него, и продолжает дальше изменять данные.
Все происходит совсем не так :) Юзер может выбрать любое кол-во объектов и записать их установки в файл, потом этот файл надо как-то загрузить (возможно в совсем др сцене) - вот придумали/установили такие правила загрузки по умолчанию. Кстати "установки" - те самые curve(s) с которыми Вы уже знакомы  :)

В предложенном решении каждый раз формируется дополнительный TFindMap (+ память, + время), хотя в лучшем случае нужный элемент найдётся в исходном контейнере менее чем за один проход, в худшем - один проход и ещё немного по наиболее подходящим элементам. И где в Вашем решении хоть какая-нибудь эффективность?
Отчаянное желание придраться  :) 

На кой нужен дополнительный TFindMap?
А это замечание обоснованное, ассоциативный контейнер - удовольствие дорогое. Но при загрузке из файла нужно чтобы объект грузился только 1 раз, т.е. найденный "лучший" надо удалять, вот и взял мапу. Определенная доля "халтуры" здесь есть, признаю - но это ж милые цветочки пор сравнению со структурами что предлагают товарищи  :)

Оно не эффективно ни по одному критерию из тех, что я приводил. Это плохое решение, переделывайте :).
Щас, разбежался  :)


Название: Re: Простой (?) поиск
Отправлено: ViTech от Сентябрь 23, 2018, 10:56
Отчаянное желание придраться  :)
Хотите сказать, что для моего варианта использования Ваше решение является очень эффективным и я всего лишь придираюсь? :)

Все происходит совсем не так :)
...
Определенная доля "халтуры" здесь есть, признаю - но это ж милые цветочки пор сравнению со структурами что предлагают товарищи  :)

А откуда товарищам знать, что всё происходит не так, как я описал, а "совсем не так"? Сколько ещё страниц в теме нужно исписать, чтобы выяснить что и как у Вас происходит? Если Вас, конечно, интересует эффективное решение задачи, а не "универсальное академическое среднее по больнице" :).


Название: Re: Простой (?) поиск
Отправлено: deMax от Сентябрь 28, 2018, 16:02
Академическое - это БД. Для случая когда непонятен текущий объем и непонятна масштабируемость. Если что, всегда можно перескочить на другую БД. или купить сервера помощнее.


Название: Re: Простой (?) поиск
Отправлено: Igors от Сентябрь 29, 2018, 08:44
Академическое - это БД.
Ой  :)  Крайность обратная "прямому перебору".

А откуда товарищам знать, что ..
Да все товарищи правильно поняли как и положено профессиональным программистам. Если данные "не лезут в память" то описание с этого и начинается. Если я об этом не упомянул - значит этой проблемы нет. Так же и с изменением данных "на лету", это совсем др задача. Стало быть речь идет о самой банальной ситуации - построить структуры для поиска один раз, и затем искать многократно. Ну и чего тратить время и место на совершенно ненужные "выяснения" (хз чего) ?

Возвращаясь к теме. Практически пробежка по всем объектам с одинаковым именем вполне устраивает. Если юзер назначил всем одинаковые имена - значит сам "злобный Буратино", пусть страдает. Просто стало интересно как же так - все ключи есть, а быстрого поиска .. строго говоря нет. Приходится или перебирать диапазон или городить ужасные структуры (что еще хуже). Вот если бы тип был bool - тогда получается, а так нет. И похоже это нормально.

Еще рассматривал такой вариант:  ключи = имя + visible + тип, А на поиске (псевдокод)
Код
C++ (Qt)
lowerBound(TKey(name, true, type))  // если найдено (name + type) - возвращаем, иначе
lowerBound(TKey(name, false, type))  // если найдено (name + type) - возвращаем, иначе
// лучший из 2
Возможно это точнее, хотя возни побольше


Название: Re: Простой (?) поиск
Отправлено: ViTech от Сентябрь 30, 2018, 12:38
Да все товарищи правильно поняли как и положено профессиональным программистам. Если данные "не лезут в память" то описание с этого и начинается. Если я об этом не упомянул - значит этой проблемы нет. Так же и с изменением данных "на лету", это совсем др задача. Стало быть речь идет о самой банальной ситуации - построить структуры для поиска один раз, и затем искать многократно. Ну и чего тратить время и место на совершенно ненужные "выяснения" (хз чего) ?

Так вот значит какая логика должна быть :).

Цитировать
Украли у мужика корову. Приходит он домой и говорит сыновьям:
- У нас корову украл какой-то мудак.
Старший брат: — Если мудак — значит маленький.
Средний брат: — Если маленький — значит из Малиновки.
Младший Брат: — Если из Малиновки — значит Васька Косой.
Все выдвигаются в Малиновку и там прессуют Ваську Косого.
Однако Васька корову не отдает. Его ведут к мировому судье.
Мировой судья:
- Ну… Логика мне ваша непонятна. Вот у меня коробка, что в ней лежит?
Старший брат: — Коробка квадратная, значит внутри что-то круглое.
Средний: — Если круглое, то оранжевое.
Младший: — Если круглое и оранжевое, то апельсин.
Судья открывает коробку, а там и правда апельсин.
Судья — Ваське Косому:
- Косой, отдай корову.


Название: Re: Простой (?) поиск
Отправлено: NoIdea от Октябрь 12, 2018, 19:23
вот чего накидал:
Ну все-таки надо думать о производительности и расходе ресурсов (хотя бы иногда  :)). Создавать многочисленные контейнеры на каждом поиске - ну это уж слишком
Если объектов десятки-сотни миллионов, тогда поиск лучше возложить на БД.
Что касается производительности, то поиск по хешу О(1) в среднем. Пересечение только замедлить может.
Ну в общем, принцип пересечения подмножеств я предложил, дальше вам решать, подойдёт ли он под конкретную задачу.
ps: у меня в проекте с 300-2000 тыс объектов подобные индексации используются, производительности хватает, памяти на индексирование до 200мб жрется.

Занятно, интересно сравнить по производительности и ресурсам с БД в памяти:
http://doc.qt.io/qt-5/qtsql-cachedtable-example.html

Код
C++ (Qt)
   QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
   db.setDatabaseName(":memory:");
   // ...



Название: Re: Простой (?) поиск
Отправлено: RedDog от Октябрь 13, 2018, 09:55
Занятно, интересно сравнить по производительности и ресурсам с БД в памяти:
http://doc.qt.io/qt-5/qtsql-cachedtable-example.html

Код
C++ (Qt)
   QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
   db.setDatabaseName(":memory:");
   // ...


На указанных объёмах данных, примерно монопенисуально, в пределах погрешности измерений, и даже файловый вариант БД приемлемые результаты показывает.
Оптимизация не идёт уже в ключе выбора хранилища данных, а идёт в ключе минимизации обращения к ним, разбиения на подмассивы (в БД отдельные таблицы), прикручивания различного распараллеливания и пр.
Удобство БД в том, что она инкапсулирует много рутинной работы, например то же пересечение множеств в любом виде, делается одной строкой.