@Hellek
Люблю говорить и слышать суть

Оптимальный алгоритм для списка задач (очередь с приоритетом). Как добавить запись в середину очереди без её смещения?

Приветствую коллеги, на днях при разработке todo list'а возникла проблема, пока не разрешенная. Задача:
Есть список из пронумерованных записей (на самом деле их несколько, но для простоты возьмем один), нумерация определяет приоритет задач. Предположим, у нас есть массив из 5 задач [0, 1, 2, 3, 4], у нас возникла необходимость добавить ещё одну задачу на 2 по важности место, в итоге всё, что начиналось с 1-цы смещается на +1. Проблема начинается с того, что мы должны сразу же писать данные в БД, в данном случае только при 1 действии будет сделан 1 insert и 5 update'ов. Теперь предположим, что у нас список из 100 записей и 2000 активных клиентов. Одно перемещение задачи через drag'n'drop влечет за собой несколько десятков перезаписей в БД, если не тысяч.
Проблемы бы не было, если приложение крутилось на клиенте и по нажатии "Сохранить" шла бы некая оптимизированная перезапись, но приложение должно быть нативным, без всяких лишних кнопок, писать нужно сразу в БД.
Костыли-варианты:
1) Новый индекс будет среднее значение между вставляемыми числами (между 3 и 4, будет вставлено 3,5. А между 3 и 3,5 -> 3,25 и т.д.). При запросе массива будет ранжирования по возрастанию, но подобные float'ы (56,21875) будут сильно забивать базу и придётся писать костыль, чтобы перезаписывать эти индексы раз в сутки на нормальные
2) Индекс будет по типу "я следую за". Выдаем произвольные id'шники записям, далее отдельным параметром говорим после кого должна идти эта запись. В таком случае "внедряя" запись в середину очереди из млн. записей мы будем вносить в БД всего лишь 2 правки - того, кто был вставлен, и того у кого-нужно обновить связь. Но тут опять же придётся писать какое-то костыльное решение для построения этих взаимосвязей при отрисовке, делать генерацию хэшей айдишников, что не лучше float'ов.
3) Делать 50, 100 и т.д. update'ов в бд
  • Вопрос задан
  • 988 просмотров
Решения вопроса 1
@Hellek Автор вопроса
Люблю говорить и слышать суть
0) Я правильно понял, что в первой таблице у нас хранятся обычные записи с id. Во второй по каждому клиенту мы храним массив по таблице вида
array(
  'priority0' => 'id345',
  'priority1' => 'id63452',
  'priority2' => 'id23',
  'priority3' => 'id9123'
)

И при изменении приоритета какой-либо из задач мы перезаписываем весь этот массив? С точки зрения лёгкости кода хороший вариант, но массивы будут довольно большие, т.к. в реальной ситуации таблиц несколько, кол-во задач по каждому человеку может доходить до нескольких сотен. Написать легко, но думаю будут проблемы с производительностью. Мне вот интересно, данную задачу ведь решили в той же Asana, да в принципе любом tasktracker-сервисе. Мне кажется что-есть какая-то математическая модель которая данную проблему как раз и решает.
1, 3 ---
2. На словах вариант красив, но пока не понятна реализация в коде. Видимо это должен быть foreach который будет формировать новый упорядоченный массив. При условии, что у нас 500 задач, это будет 500 циклов, пока сложно сказать, каким именно образом будет идти сопоставление, есть некоторые сомнения, что это будет супер быстро. Думаю, насколько это конкурентно по быстродействию с остальными вариантами.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
x67
@x67
может изменить структуру данных - хранить в одной таблице запись-ключ, а в другой для каждого клиента хранить матрицу или словарь с парами ключ - приоритет(ключ уникален для задания, значит мы получим сопоставление задания с приоритетом). Каждое добавление записи/задания влечет добавление записи в таблицу 1 и изменение записи в таблице 2. Изменение приоритета задания влечет только одно изменение записи в таблице 2. Сортировка, сопоставление происходит на стороне клиента, на сервер посылается уже готовый новый словарь. Каждый запрос будет больше весить, но что нам 1 запрос размером в 15 пар ключ-приоритет для среднего пользователя?
1 ваш вариант, имхо, сферический костыль, консервированный в жидком вакууме
2 вариант красив, но его нужно проработать и просчитать
3 вариант вообще не вариант естественно)
Ответ написан
Комментировать
@vasiliev
Задавать приоритеты как (0,100,200,300,400). Если надо вставить задачу между двумя другими, устанавливать её приоритет как округлённое до целого среднее. Если оказалось очень много записей и полученное число совпадает с одним из соседних, проводить пересчет приоритетов.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы