Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Октябрь 06, 2019, 16:01



Название: Быстрая вставка
Отправлено: Igors от Октябрь 06, 2019, 16:01
Добрый день

1) Есть такой контейнер
Цитировать
Sphere 1
Cube 1
Cube 3
Sphere 2
Sphere 5
Т.е. базовые имена (Sphere и Cube) идут в произвольном порядке, а вот номера уникальны (в пределах базового имени) и идут по возрастающей. Нужно вставить в этот контейнер другой с теми же базовыми именами но произвольными индексами, напр
Цитировать
Cube 2
Cube 3
Cube
Cube
Sphere 1
В рез-те должно получиться
Цитировать
Sphere 1   // override
Cube 1     
Cube 2      // insert   
Cube 3      // override
Cube 4      // insert with new index
Cube 5      // insert with new index
Sphere 2
Sphere 5
Не смог придумать способ без перебора  :'(  Создавать любые временные контейнеры можно

2)  Все то же, но первый контейнер - такое дерево
Цитировать
Sphere 1
  Cube 1
    Cone 1
Sphere 2
  Cube 2
Sphere 5
  Cube 5
    Cone 5

Спасибо


Название: Re: Быстрая вставка
Отправлено: Racheengel от Август 07, 2020, 12:24
Контейнер проиндексировать придется, естественно.
Что то типа QMap<QString, QPair<int,int>> то есть <key, <number,line>>
Например будет так для примера 1:
<Sphere <1,1><2,4><5,5>>
<Cube <1,2><3,3>
Ну а дальше уже дело техники.


Название: Re: Быстрая вставка
Отправлено: Авварон от Август 07, 2020, 13:00
1. отсортировать оба массива так чтобы номера без индексов были в конце пачки (а не в начале). например, можно для удобства сперва декомпозироваь строку на пару - ключ/номер
2. параллельно пробежаться по массивам склеивая сортированные пачки - примерно как делает std::merge/std::set_union и друзья, только помимо склейки надо считать кол-во элементов в "пачке" и если встретился элемент без номера, присваивать ему новый номер.

Хорошая задачка на собеседование.
Интересно можно ли за O(M+N) сделать?..


Название: Re: Быстрая вставка
Отправлено: Igors от Август 08, 2020, 12:23
Контейнер проиндексировать придется, естественно.
Что то типа QMap<QString, QPair<int,int>> то есть <key, <number,line>>
Например будет так для примера 1:
<Sphere <1,1><2,4><5,5>>
<Cube <1,2><3,3>
Ну а дальше уже дело техники.
Наверное значение мапы - вектор пар. В любом случае если line - индекс строки, то он "уплывет" после первой же вставки

параллельно пробежаться по массивам склеивая сортированные пачки - примерно как делает std::merge/std::set_union и друзья,
Дело осложняется тем что пачек (по первому ключу) может быть сколько угодно, и они идут не подряд

Наверно сначала нужно упаковать идущие подряд, т.е.

Sphere <1, 1>
Cube <1, 1>
Cube <3, 3>
Sphere <2, 2>
Sphere <5, 5>

Теперь нужно вставить "Cube 2", возможно ли его найти пачку для него в массиве выше используя lower_bound ? Очевидно "просто так" нет, мешает первый ключ. А может как-то "непросто"?


Название: Re: Быстрая вставка
Отправлено: RedDog от Август 08, 2020, 15:42
а если сначала на бд потренироваться?
а потом бустовые индексы прикрутить.


Название: Re: Быстрая вставка
Отправлено: Igors от Август 09, 2020, 09:12
а если сначала на бд потренироваться?
а потом бустовые индексы прикрутить.
Ну как-то оно смотрится "инородным телом", да и хлопот с "прикручиванием" немало. Кстати не знаю умеет ли БД такое делать



Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 01, 2020, 14:55
Хорошая задачка на собеседование.
Интересно можно ли за O(M+N) сделать?..

Принципиально отказываюсь от собеседований "с задачами", особенно с такой идеологией.
Как бы "шашечки или ехать"?


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 01, 2020, 15:17
Принципиально отказываюсь от собеседований "с задачами", особенно с такой идеологией.
Как бы "шашечки или ехать"?

Если кандидат не в состоянии распознать и написать set_union/set_intersect, то зачем такой кандидат нужен? Я, собственно, люблю задачки которые в завуалированном виде мапятся на стандартный алгоритм - тогда даже не требуется реализовывать этот алгоритм, главное чтобы кандидат понял что это конкретный алгоритм, сумел преобразовать данные к виду в котором алгоритм применим, и применил его. Как follow up можно поговорить о том, как алгоритм устроен, в простых случаях (как этот) можно и попросить написать. Или уже параллельно пробежаться по двум массивам оказывается непосильной задачей?


Название: Re: Быстрая вставка
Отправлено: Пантер от Сентябрь 01, 2020, 18:30
Хорошая задачка на собеседование.
Интересно можно ли за O(M+N) сделать?..

Принципиально отказываюсь от собеседований "с задачами", особенно с такой идеологией.
Как бы "шашечки или ехать"?

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


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 01, 2020, 18:39
Я тоже отказываюсь. Несколько собеседований у меня закончились толком не начавшись. Зато время сэкономил.

Вот потому ты и не в гугле=) Впрочем, я тоже :(

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


Название: Re: Быстрая вставка
Отправлено: Пантер от Сентябрь 01, 2020, 19:10
Я тоже отказываюсь. Несколько собеседований у меня закончились толком не начавшись. Зато время сэкономил.

Вот потому ты и не в гугле=) Впрочем, я тоже :(

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

Это проблема гугла, не находишь? ;D


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 02, 2020, 11:53
...в простых случаях (как этот) можно и попросить написать. Или уже параллельно пробежаться по двум массивам оказывается непосильной задачей?
А как пробегаться? Напр исходный массив
Цитировать
Cube 1
...
И нужно вставить "Cube 10". Я обязан просматривать исходный массив пока не обнаружу "Cube >= 10" или до упора. Только после этого могу вставлять или реплейсить. И после первой же вставки номера строк изменятся, как-то сохранять их (для 2 и более вставляемых) не выходит


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 03, 2020, 09:18
Я, собственно, люблю задачки которые в завуалированном виде мапятся на стандартный алгоритм
Кстати здесь есть такая (или подобная) возможность. Как работает пресловутый O(log(N)): отрезок (контейнер) делится пополам, и, в зависимости от рез-та сравнения, операция повторяется с левой или правой частью.

Здесь прямолинейно использовать такое нельзя, мешает первый ключ. Но можно немного изменить стандартный алгоритм:

- делим отрезок пополам, если первый ключ "наш" ("Cube" в примере выше), то продолжаем как обычно. Иначе рекурсивно ищем в обеих частях. Правда нужно немного помозговать как сравнить рез-ты обеих частей - но это решаемо. Конечно эффективность будет не log(N), а меньше в зависимости от числа первых ключей. Но решение 100% "общее"



Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 03, 2020, 11:01
Я же написал - предварительно отсортировать так что номера без номеров будут в конце. Тогда присвоить им уникальные номера - задача тривиальная.
Ну да, типа будет N*log(N) сортировка - надо быстрее - давайте обсудим.


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 03, 2020, 11:42
Я же написал - предварительно отсортировать так что номера без номеров будут в конце. Тогда присвоить им уникальные номера - задача тривиальная.
Вставляемые "без номеров" - просто доп случай, это ничего не меняет

Ну да, типа будет N*log(N) сортировка - надо быстрее - давайте обсудим.
Как сделано сейчас: для каждого вставляемого (напр "Cube 10") просматривается весь массив до обнаружения "Cube >= 10", наилучшая позиция вставки запоминается. Напр нашли "Cube 5" в позиции (по-простому "в строке") 44, значит фиксируем: место вставки 44+1 = 45. Но еще не вставляем, ведь может найдется "Cube 7" и.т.п. Наконец весь массив просмотрен, вставляем в запомненное место, если оно -1 (вообще нет никаких "Cube"), то просто в конец.

Сейчас это вполне устраивает, необходимости выжимать скорость здесь нет. Но все-таки "перебор = визитная карточка лоха", интересно как без него. Не менее (а может и более) интересно как это сделать "технично", не размазывая текст на много страниц.


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 03, 2020, 11:51
Если кандидат не в состоянии распознать и написать set_union/set_intersect, то зачем такой кандидат нужен?

Да мало ли. Гуйню программить, интерфейсы для филдбасов всяких, дазы банных etc.

Просто ИМХО задачи на собеседованиях нивелируют саму идею со-БЕСЕДОВАНИЯ. Т.е. слово подразумевает беседы, общение, выявление интересов, соответствие предпочтений и предложений. А не экзамен с высоким уровнем вероятности удачи-неудачи.

Скиллы? Они из резюме видны. Человека можно спросить в конце концов, чем он раньше занимался. Если не "прыгун" - расскажет сам с удовольствием. А случайный пассажир или тупить будет, или наоборот про воздушные замки распинаться - но таких сразу видно.

Задачи - да пожалуйста. Только на испытательном сроке, когда уже контракт подписан. Но не раньше.


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 03, 2020, 12:01

Просто ИМХО задачи на собеседованиях нивелируют саму идею со-БЕСЕДОВАНИЯ. Т.е. слово подразумевает беседы, общение, выявление интересов, соответствие предпочтений и предложений. А не экзамен с высоким уровнем вероятности удачи-неудачи.


Если вы 40 минут будете молчать а потом гордо скажете "я сделяль", то вас не возьмут, даже если ваше решение оптимальное.
Цель задач - не только решить их, а посмотреть как кандидат думает, рассуждает, какие трейд-оффы делает.


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 03, 2020, 16:27
На самом деле, сортировку можно сделать за линейное время, у нас же индексы известны, по сути это недосортировка подсчетом.
Бежим по массиву. Встретили cube_5 положили в hash["cube"][5], встретили "cube", положили в hash["cube"][-1] (мультимапа нужна). Теперь мы знаем для каждого подхэша количество элементов, "развернуть" в прямой хэш задача нетрудная.
Ну а дальше у нас есть "массивы" (хэши) "сортированные" по индексу - смержить их задача тоже тривиальная.
В общем за О(Н) делается, какие конкретно структуры данных применить (можно вместо внутреннего хэша использовать вектор) оставлю внимательному читателю.


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 04, 2020, 12:13
В общем за О(Н) делается, какие конкретно структуры данных применить (можно вместо внутреннего хэша использовать вектор) оставлю внимательному читателю.
Ага, "хвостиком махнул" :)

Похоже я просто чуть "недожал"
Как сделано сейчас: для каждого вставляемого (напр "Cube 10") просматривается весь массив до обнаружения "Cube >= 10", наилучшая позиция вставки запоминается. Напр нашли "Cube 5" в позиции (по-простому "в строке") 44, значит фиксируем: место вставки 44+1 = 45. Но еще не вставляем, ведь может найдется "Cube 7" и.т.п. Наконец весь массив просмотрен, вставляем в запомненное место, если оно -1 (вообще нет никаких "Cube"), то просто в конец.
Все это можно делать пробегая исходный массив один раз для всех вставляемых
Код
C++ (Qt)
// строка + место вставки + индекс
using TLine = std::tuple<QString, int, int>;
inline QString & Name( TLine & line ) { return std::get<0>(line); }
inline int & InsP( TLine & line ) { return std::get<1>(line); }
inline int & Index( TLine & line ) { return std::get<2>(line); }
 
// исходный массив и вставляемый
std::vector<TLine> src, arrived;
...
// сортируем вставляемых
for (auto & a : arrived)
 InsP(a) = -1;
std::sort(arrived.begin(), arrived.end());
 
// для каждой строки исходного обновляем места вставки всех
// вставляемых с тем же первым ключом
for (size_t i = 0; i < src.size(); ++i) {
 auto it = std::lower_bound(arrived.begin(), arrived.end(), src[i]);
 for (; it != arrived.end(); ++it) {
   if (Name(src[i]) != Name(*it))) break;
   if (Index(*it) >= Index(src[i]))
       InsP(*it) = i;
 }
}
 
Теперь сортируем вставляемых по месту вставки и вставляем накапливая смещение
Код
C++ (Qt)
std::sort(arrived.begin(), arrived.end(),  [](const TLine & a, const TLine & b) -> bool
{
   return InsP(a) < InsP(b);
});
 
int offset = 0;
for (const auto & a : arrived) {
 if (insP(a) < 0)
  src.push_back(a);
 else {
  int insP = InsP(a) + offset;
  assert(Name(a) == Name(insP));
  if (Index(src[index] == Index(a))  // override
    src[index] = a;
  else {
   src.insert(src.begin() + insP + 1, a);  // insert after
   ++offset;
  }
}
}
 
Писал здесь и tuple юзал первый раз, так что может где-то и насвистел

Ну а дальше уже дело техники.
Да, но хотелось бы эту технику видеть. А "генератором идей" я и сам могу..  :)


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 04, 2020, 14:15
Я накидал линейный алгоритм с кучей дополнительной памяти и аллокаций. Но тут нарушено требование что ключи в результирующем контейнере идут в порядке возрастания. В остальном все как надо - ключи уникальны, дырки заполняются.
Поправить сортировку можно, гм, отсортировав в последнем цикле или используя линейно бегущий индекс как счетчик при вставке. Оба варианта убивают всю линейность, особенно второй в случае, если есть большая дырка (как в примере со sphere 5000).
Так что можно не выпендриваться и сразу писать как я изначально предлагал через слияние сортированных массивов. Может быть "продемонстрирую навыки" позже.

Код:
#include <algorithm>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
#include <unordered_map>

#include <assert.h>

int main(int argc, char *argv[])
{
    struct Line {
        std::string name;
        std::optional<int> index;
    };

    using Lines = std::vector<Line>;

    Lines source{
        {"sphere", 1},
        {"cube", 1},
        {"cube", 3},
        {"sphere", 2},
        {"sphere", 5},
    };
    Lines arrived{
        {"sphere", 1},
        {"cube", 2},
        {"cube"},
        {"cube"},
        {"sphere", 1},
        {"sphere", 5000},
    };

    // usually, LinesInnerHash contains exactly 1 element in the vector => so we can use
    // QVarLengthArray<Line, 1> here to avoid allocations. The only case when we need multiple
    // elements is to store linex with no index assigned, i.e. when key is std::nullopt
    using LinesInnerHash = std::unordered_map<std::optional<int>, Lines>;
    using LinesHash = std::unordered_map<std::string, LinesInnerHash>;

    // first, divide lines for by their name, O(N), N == source.size()
    LinesHash sourceHash;
    for (auto line: source) {
        assert(line.index); // source array should not contain unindexed elements
        sourceHash[line.name][line.index] = {line};
    }

    LinesHash arrivedHash;
    for (auto line: arrived) { // O(K), K == arrived.size()
        // arrivedHash[line.name][std::nullopt] will contain all unindexed elements
        arrivedHash[line.name][line.index].push_back(line);
    }

    // now, we need to index unindexed lines
    for (auto &item: arrivedHash) { // totally ~O(K) ?
        auto &innerHash = item.second;
        int index = 1;
        // extract unindexed lines from the hash
        Lines unindexedLines(std::move(innerHash[std::nullopt]));
        innerHash.erase(std::nullopt);
        for (auto &line: unindexedLines) {
            // find first free index
            while (innerHash.find(index) != innerHash.end())
                index++;
            line.index = index; // update line
            innerHash[index] = {line}; // and insert it with the new index
            index++; // not necessary, small optimisation to avoid extra find() call
        }
    }

    for (const auto &item: arrivedHash) { // O(K)
        auto &sourceInnerHash = sourceHash[item.first];
        auto &arrivedInnerHash = item.second;
        for (const auto &subItem : arrivedInnerHash) {
            // if we use unordered_map as an inner container, this will be simply
             sourceInnerHash[subItem.first] = std::move(subItem.second);
        }
    }

    Lines result;
    for (const auto &item: sourceHash) { // O(K)
        auto &innerHash = item.second;
        for (const auto &innerItem: innerHash)
            result.push_back(innerItem.second.front());
    }

    for (const auto &line: result) {
        std::cout << line.name << " " << *line.index << std::endl;
    }

    return 0;
}

Upd: только он не работает, я налажал со дырками=)


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 04, 2020, 20:32
Ладно, вот логарифмеческий с мержем, тоже пришлось повозиться с вытираторами, но идея простая.
Сперва сливаем все элементы с одинаковым ключом в массив так что сперва идут новые. Дальше сортируем массив стабильной сортировкой так чтобы неиндексированные элементы были в конце. Так как сортировка стабильная, то парные ключи сохраняют порядок новый-старый. Делаем трюк с std::unique - удаляем дупликаты которые имеют одинаковые номера. При этом важно исключить непроиндексированные так как у них одинаковый (пустой ключ). Так как мы идем с начала массива, то будет оставлен "новый" ключ (помним про порядок старый-новый), а старый удален.
Ну а дальше самое сложное - назначить индексы непроиндексированным элементам (лежат по-прежнему в конце). Тут пришлось повозиться=) оказалось ошибка вообще в другом месте, как всегда.

Код:
#include <algorithm>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
#include <unordered_map>

#include <assert.h>

struct Line {
    std::string name;
    std::string payload;
    std::optional<int> index;
};

using Lines = std::vector<Line>;

void logarithmic(const Lines &source, const Lines &arrived)
{
    using LinesHash = std::unordered_map<std::string, Lines>;

    // first, divide lines for by their name, O(N), N == source.size()
    LinesHash hash;
    for (auto line: arrived) {
        hash[line.name].push_back(line);
    }
    for (auto line: source) {
        assert(line.index); // source array should not contain unindexed elements
        hash[line.name].push_back(line);
    }

    Lines result;
    for (auto &item: hash) {
        auto &lines = item.second;

        auto lessThan = [](const Line &lhs, const Line &rhs) { // std::nullopt should be last
            if (lhs.index && rhs.index)
                return *lhs.index < *rhs.index;
            if (!lhs.index && !rhs.index)
                return false;
            return lhs.index && !rhs.index;
        };
        std::stable_sort(lines.begin(), lines.end(), lessThan); // N * log(N)

        // find position of the last existing element and the number of elements
        const auto noIndex = [](const auto &line) { return !line.index; };
        auto mid = std::find_if(lines.begin(), lines.end(), noIndex); // O(N)
        const auto unindexedCount = std::distance(mid, lines.end());

        // remove duplicates between the first existing element and the last one
        // this effectively implements "override" logic
        const auto equals = [](const auto &lhs, const auto &rhs) {
            return lhs.index == rhs.index;
        };
        const auto newEnd = std::unique(lines.begin(), mid, equals); // O(N)
        lines.erase(newEnd, mid); // O(N)

        Lines indexedLines;
        indexedLines.reserve(lines.size());

        // assign indexes to elements in the [mid..end) range
        mid = lines.end() - unindexedCount; // erase invalidates iterator, re-assign it
        int index = 1;
        auto it = lines.begin();
        for (auto jt = mid; it != mid || jt != lines.end(); ) { // O(N)
            if (jt == lines.end() || it->index == index) {
                indexedLines.push_back(*it);
                ++it;
            } else {
                jt->index = index;
                indexedLines.push_back(*jt);
                ++jt;
            }
            ++index;
        }

        // add current chunk to the result
        result.insert(result.end(), indexedLines.begin(), indexedLines.end()); // O(N)
    }

    for (const auto &line: result) {
        std::cout << line.name << " " << line.payload;
        if (line.index)
            std::cout << " " << *line.index;
        std::cout << std::endl;
    }
}

int main(int argc, char *argv[])
{
    Lines source{
        {"sphere", "old", 1},
        {"cube", "old", 1},
        {"cube", "old", 3},
        {"sphere", "old", 2},
        {"sphere", "old", 5},
    };
    Lines arrived{
        {"sphere", "new", 1},
        {"cube", "new", 2},
        {"cube", "new"},
        {"cube", "new"},
        {"sphere", "new", 4},
        {"sphere", "new"},
        {"sphere", "new", 5000},
    };

    logarithmic(source, arrived);
    return 0;
}



Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 05, 2020, 09:38
Ну во-первых с меня рабочий пример (аттач). Идея выше, но почистил и выкинул tuple (здесь не очень лепится)
Я накидал линейный алгоритм с кучей дополнительной памяти и аллокаций. Но тут нарушено требование что ключи в результирующем контейнере идут в порядке возрастания.
Ну если бы не было (мерзкого) первого ключа (строки "Cube", "Sphere"), то и проблем/трудностей бы никаких не было, банальная/тривиальная сортировка - и все дела. А тут есть произвольный порядок следования по первому ключу

- "группа" (или "кучка") Sphere
- группа Cube
- группа Sphere
и.т.п.

И его надо сохранить, можно добавлять в группы, но не менять их порядок следования. Бросается в глаза решение: создать ассоциативные контейнеры для каждого первого ключа (строки). Тогда да, вставляемые в них прекрасно лягут, но.. дальше что? Я не вижу след хода, ведь инфы о "группах" нет и как ее заполучить - хз

Ладно, вот логарифмеческий с мержем, тоже пришлось повозиться с вытираторами, но идея простая.
Сперва сливаем все элементы с одинаковым ключом в массив так что сперва идут новые. Дальше сортируем массив стабильной сортировкой так чтобы неиндексированные элементы были в конце. Так как сортировка стабильная, то парные ключи сохраняют порядок новый-старый. Делаем трюк с std::unique - удаляем дупликаты которые имеют одинаковые номера. При этом важно исключить непроиндексированные так как у них одинаковый (пустой ключ). Так как мы идем с начала массива, то будет оставлен "новый" ключ (помним про порядок старый-новый), а старый удален.
Ну а дальше самое сложное - назначить индексы непроиндексированным элементам (лежат по-прежнему в конце). Тут пришлось повозиться=) оказалось ошибка вообще в другом месте, как всегда.
Простите, но "ни асилил" (хотя я и хорошо знаком с задачей). Не исключено что это я туплю, но может и объективно быть слишком сложно.

По поводу "непроиндексированных". Зачем включать это в алгоритм, не лучше ли сделать это пре-проходом, типа "нет индекса - назначим". Какой назначать - в постановке этого нет, значит на усмотрение разработчика. Я использовал новые индексы "от максимального" (в обоих массивах). Можно и "заполнять дырки", это та самая задача генерации уникального ID (о которой был разговор и которую Вы охаяли  :))

Ну и сам алгоритм - не вижу как Вы сохраняете исходный порядок по первому ключу. И этот "контейнер на контейнер" - оно конечно "не запрещено", но хорошего впечатления не производит.


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 05, 2020, 12:27
Ну и сам алгоритм - не вижу как Вы сохраняете исходный порядок по первому ключу.

Про это в постановке задачи ничего не было. Сохранение порядка начинает вызывать слишком много вопросов. Допустим мы начинаем генерить ключ с максимального из существующих. Тогда при слиянии все элементы будут добавляться в последнюю группу, то есть без разрывов. Внимание вопрос, а откуда разрыв-то появляется? Что мешает сделать сортированный массив по обоим ключам?
Если у вас несортированный массив с требованием сохранять порядок - ну тут ничего кроме перебора особо не придумаешь.


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 05, 2020, 14:06
Про это в постановке задачи ничего не было.
Та было (вернее есть). И словами, и примером
Сохранение порядка начинает вызывать слишком много вопросов. Допустим мы начинаем генерить ключ с максимального из существующих. Тогда при слиянии все элементы будут добавляться в последнюю группу, то есть без разрывов. Внимание вопрос, а откуда разрыв-то появляется? Что мешает сделать сортированный массив по обоим ключам?
Приложение имеет специфический 3D объект: ("instancer"), контейнер др 3D объектов ("instances"). Смысл очевиден: хранить один базовый "Cube" и один "Sphere", для каждого инстанса уникальны лишь данные трансформа/анимации. Инстансы упорядочены в порядке их создания, это имеет значение. Имена инстансов уникальны, в этом смысл "индексов"

Ну и конечно набегает ф-ционал import/export, т.е. извлечь инстансы из инстансера (получить "обычные" объекты с которыми можно работать индивидуально), и наоборот, поместить в инстансер. Причем помещать можно не только ранее извлеченные, требуется чтобы сбивалось базовое имя и геометрия. Ну вот и имеем то что имеем

Ага, пошли "разговоры о задаче", "как всегда" корявая постановка и.т.п. :)

Если у вас несортированный массив с требованием сохранять порядок - ну тут ничего кроме перебора особо не придумаешь.
Как в том анекдоте
Цитировать
Ну уж после 3 стаканов Вы бы точно так работать не смогли!!


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 10, 2020, 12:11
Если вы 40 минут будете молчать а потом гордо скажете "я сделяль", то вас не возьмут, даже если ваше решение оптимальное.
Цель задач - не только решить их, а посмотреть как кандидат думает, рассуждает, какие трейд-оффы делает.

Если человек пришел и 40 минут тупо молчит и ничего не рассказывает, это уже как то в общем подозрительно... Туда ли он вообще пришел?

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


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 10, 2020, 13:47
Racheengel, я вижу, перешел на "работу с людьми", какие-то там вставки его уже не интересуют :)
Но я хоть убей не понимаю этого тренда с "задачами". Что хотят от человека? чтобы он алгоритмы на доске писал или чтоб работу работал?
Бизнес-задачу за полчаса "на коленке" он не решит, это надо специфику сперва понимать.
А по "тестовому примеру" мало что можно выяснить. Ну разве что если на "джуна" человек идет.
И что, давайте брать всех с улицы? Это еще хорошо если потом от него легко избавиться.

Что хотят от человека?
А действительно, что Вы от него хотите? Какую работу он должен делать? Если тестовая задача с этим связана - она вполне к месту. А оценить какие-то общие познания - можно и без нее


Название: Re: Быстрая вставка
Отправлено: RedDog от Сентябрь 10, 2020, 14:18
А по "тестовому примеру" мало что можно выяснить. Ну разве что если на "джуна" человек идет.
Подобные этой теме примеры решать в коде не надо на собеседовании.
А вот понять, по рассуждениям может ли чел мыслить в нужном направлении, вполне возможно.
Если кандидат заявляет что "работал с большими объемами данных" а в рассуждениях над подобными примерами молчит, то можно на счет него сделать неутешительный вывод.
PS: но на самом деле, для собеседования данный пример немного жестковат.


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 10, 2020, 15:46
Подобные этой теме примеры решать в коде не надо на собеседовании.
А вот понять, по рассуждениям может ли чел мыслить в нужном направлении, вполне возможно.
Если кандидат заявляет что "работал с большими объемами данных" а в рассуждениях над подобными примерами молчит, то можно на счет него сделать неутешительный вывод.
PS: но на самом деле, для собеседования данный пример немного жестковат.

И я про то же. Пусть человек "выговорится". Если "сам делал" - расскажет, что да как. Если только чтоб "вайти" - и без примера отвалится.
P.S. если не отвалится, то дорога ему в продажники, а не в девелоперы :)


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 10, 2020, 15:52
Но я хоть убей не понимаю этого тренда с "задачами". Что хотят от человека? чтобы он алгоритмы на доске писал или чтоб работу работал?
Бизнес-задачу за полчаса "на коленке" он не решит, это надо специфику сперва понимать.
А по "тестовому примеру" мало что можно выяснить. Ну разве что если на "джуна" человек идет.

Ну смотрите, вы даете задачку реализовать баннерокрутилку. У вас есть несортированный набор рекламных объявлений с приоритетом. Вам надо показать топ-10 по высшему приоритету. Прикладная задача? Вполне.
Как решать? Можно отсортировать и взять топ 10 (N*logN + C). Если кандидат умный то он знает про std::partial_sort (N*logK, K == 10 в примере). Если он решал задачки на литкоде, то знает как сделать за (примерно) линейное время (quickselect).
Вполне себе бизнес задача на 40 минут (ну ладно, квиксорт\квикселект я за 40 минут не напишу да и не нужно это, но если кандидат упомянет такое решение - это плюс, собственно тут обсуждение и нужно).


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 10, 2020, 15:56
Racheengel, я вижу, перешел на "работу с людьми", какие-то там вставки его уже не интересуют :)

"Вставок без людей не бывает" :)

Ну а по теме, действительно, условие по сохранению "порядка" - оно новое. Поэтому и решение меняется.
Код лень писать, но я бы сначала сделал кэш по первичному ключу в виде <имя:<номер:позиция>>  ( QMap<QString, QMap<int, int> >)





Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 10, 2020, 16:01
Ну смотрите, вы даете задачку реализовать баннерокрутилку. У вас есть несортированный набор рекламных объявлений с приоритетом. Вам надо показать топ-10 по высшему приоритету. Прикладная задача? Вполне.

Вполне прикладная. Только не на 40 минут.
1. Какую технологию используем для показа баннеров?
2. В каком формате хранятся объявления?
3. С помощью каких средств осуществляется доступ к объявлениям?
4. Показать за какой период времени? Все 10 сразу или по очереди? С какой задержкой?
5. Куда деплоить?
Ну и еще десяток попутных задач.
В общем, новичку на неделю работы (моя грубая оценка, т.к. я по вебу не спец).

PS. Ну и кстати я бы за полную сортировку голосовал.
Пускай все объявления всегда по приоритетам лежат отсортированными.
Так и искать проще, и вставлять, и удалять.


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 10, 2020, 16:34
Ну а по теме, действительно, условие по сохранению "порядка" - оно новое. Поэтому и решение меняется.
Код лень писать, но я бы сначала сделал кэш по первичному ключу в виде <имя:<номер:позиция>>  ( QMap<QString, QMap<int, int> >)
Цитировать
Мудрость приходит со старостью, но старость чаще приходит одна
:)


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 10, 2020, 16:38

Вполне прикладная. Только не на 40 минут.

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


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 10, 2020, 16:42
Вполне прикладная. Только не на 40 минут.
1. Какую технологию ...
Кстати да. Дело совсем не в том знает ли человек std::partial_sort, а насколько хорошо он владеет используемыми технологиями. Ну или насколько быстро и охотно он их освоит (бессмертное "быстро учуся"). Столь общие/абстрактные задачки как эта бывают, но редко, макс 5%, остальное завязано на специфику


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 10, 2020, 16:53
Кстати да. Дело совсем не в том знает ли человек std::partial_sort, а насколько хорошо он владеет используемыми технологиями. Ну или насколько быстро и охотно он их освоит (бессмертное "быстро учуся"). Столь общие/абстрактные задачки как эта бывают, но редко, макс 5%, остальное завязано на специфику

Если человек, идущий на сеньора до сих пор не выучил алгоритмы stl, то может он их никогда не выучит=)


Название: Re: Быстрая вставка
Отправлено: Пантер от Сентябрь 10, 2020, 17:47
Кстати да. Дело совсем не в том знает ли человек std::partial_sort, а насколько хорошо он владеет используемыми технологиями. Ну или насколько быстро и охотно он их освоит (бессмертное "быстро учуся"). Столь общие/абстрактные задачки как эта бывают, но редко, макс 5%, остальное завязано на специфику

Если человек, идущий на сеньора до сих пор не выучил алгоритмы stl, то может он их никогда не выучит=)
Ибо это нафиг не нужно :) Я сеньор-помидор и я не знаю алгоритмов stl. Знаю только про sort, юзал его когда-то.


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 10, 2020, 17:56
Если человек, идущий на сеньора до сих пор не выучил алгоритмы stl, то может он их никогда не выучит=)
Ничего страшного. Действительно хороших алгоритмов там очень немного, в основном упорное навязывание ФП


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 10, 2020, 18:10

Ибо это нафиг не нужно :) Я сеньор-помидор и я не знаю алгоритмов stl. Знаю только про sort, юзал его когда-то.

Если ваша работа - перекладывать джисоны то, возможно, не стоит эти гордиться ;)
Но если серьезно, то вон всякие set_intersection и unique весьма полезны. Как удалить дубликаты / из пути? Ну можно написать регэксп о который будет спотыкаться каждый читающий, а можно просто unique сделать - это будет в 100500 раз быстрее.


Название: Re: Быстрая вставка
Отправлено: Пантер от Сентябрь 10, 2020, 19:11
Я редко когда встречал проблему, где узким местом было использование неправильного алгоритма сортировки. Обычно проблемы (и и решения) находятся на более высоких уровнях абстракции.


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 10, 2020, 19:13
"не барское это дело, синьйорам в stl-ях копаться" :)

Ну а если серьёзно, многие забивают на производительность, к сожалению. И дело тут даже не в регэкспах (хотя они write only как правило). Электрон и иже с ним развращают. Можно ж больше памяти запихнуть и проц побыстрей, и всё, типа.

PS. Имхо: алгоритмические штуки - это всё-таки middle-level. Синьйор как раз тем и отличается, что может делегировать подобные задачи нужным людям, а не заниматься микроменеджментом и копипастингом.


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 10, 2020, 19:27
Я редко когда встречал проблему, где узким местом было использование неправильного алгоритма сортировки. Обычно проблемы (и и решения) находятся на более высоких уровнях абстракции.

Значит ты не работал с данными где миллионы строк. Банальное знание алгоритмов и О-нотации позволило мне в яндексе ускорить джобу с 12 часов до 3. Джоба молотила всего какие-то жалкие 30-40 терабайт данных.
Банерокрутилка - хороший пример, у тебя миллионы банеров, а надо только 10. 10^6*log(10^6) это сильно больше чем 10^6*log(10)


Название: Re: Быстрая вставка
Отправлено: Пантер от Сентябрь 10, 2020, 19:41
ХЗ, я всего лишь в IoT работаю. Баннеров тут нет...


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 10, 2020, 22:32
Банерокрутилка - хороший пример, у тебя миллионы банеров, а надо только 10. 10^6*log(10^6) это сильно больше чем 10^6*log(10)

Имхо, сейчас это хороший пример плохого дизайна...
Миллион баннеров, не отсортированных по темам/приоритету, из которого надо каждый раз вынимать по N штук "лучших"? ну :(
Почему бы их не сортировать по мере поступления?
Или, если невозможно, хотя бы там раз в час в бэкграунде?


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 10, 2020, 22:46
Ну вы же не будете сортировать гигабайтный файл по каждому возможному ключу? Потом такое решение экономит время разработки - вам не надо создавать один еще один файл, тестировать, аплоадить его на кластера... 3 строки кода или два дня работы?
Как я уже говорил - так могло исторически сложиться, может там вообще заммапленный в память файл на каком-нибудь богомерзком MMS (https://github.com/yandex/mms) и вам еще надо сериализацию написать для сортированной версии.


Название: Re: Быстрая вставка
Отправлено: RedDog от Сентябрь 11, 2020, 11:10
Алгоритмами можно оптимизировать, ну процентов на 10% по производительности, самая главная оптимизация в бизнес логике идет, там можно в десятки тысяч раз оптимизировать.
Была задача сначала написана "в лоб" бегала по графу 30-40 млн раз, после оптимизации (предварительной фильтрации) по этому же графу стала бегать 3-5 тыс. раз. Но для этого надо было сначала распарсить бизнес-логику.


Название: Re: Быстрая вставка
Отправлено: Igors от Сентябрь 11, 2020, 12:12
Банальное знание алгоритмов и О-нотации позволило мне ..
Согласен с первой частью (знание алгоритмов), но что хорошего в "О-нотации"? По-моему это такой же атавизм как "бок-схемы", чисто для ВУЗов где надо же как-то контролировать учащихся. Как это Вам помогло?  :)


Название: Re: Быстрая вставка
Отправлено: Авварон от Сентябрь 11, 2020, 12:24
У вас цепочка мапперов редьюсеров которые маппят маппят маппят редюсят маппят редюсят. Было бы неплохо понимать где узкое место и как его исправить.


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 11, 2020, 13:56
Алгоритмами можно оптимизировать, ну процентов на 10% по производительности

Ну, "банальное" распараллеливание может дать от 300% и выше...


Название: Re: Быстрая вставка
Отправлено: Racheengel от Сентябрь 11, 2020, 13:56
У вас цепочка мапперов редьюсеров которые маппят маппят маппят редюсят маппят редюсят. Было бы неплохо понимать где узкое место и как его исправить.

Узкое место - маперы и редьюсеры. Может быть, их не стоит использовать в цепочке? :)