Задать вопрос
Ответы пользователя по тегу Алгоритмы
  • Как использовать квадро-дерево, если его элементы необходимо изменять?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    В дерево можно добавлять вершины и удалять их. При изменении удаляйте старый элемент и добавляйте измененный. Не забудьте удалить и пучтые проиежуточные вершины.
    Ответ написан
  • Как сгенерировать непрерывные случайные величины с заданным законом распределения?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Во-первых, компьютеры вообще не могут в непрерывность. Так что вы получите лишь некоторое приближение: дискретную случайную величину с большим количеством возможных рациональных значений. Более того, компьютеры не умеют и в случайность, поэтому вы получите лишь псевдо-случайные числа.

    Обычно, сначала реализуют дискретную случайную равномерно распределенную величину. Гуглите алгоритмы генерации псевдослучайных чисел, если вам нельзя даже какой-нибудь rand() использовать.

    Далее получают равномерно распределенную случайную величину на отрезке [0,1]. Для этого генерируют случайное число от 0 до MAX_RAND и делят на MAX_RAND.

    Произвольную же случайную величину можно получить, воспользовавшись свойством функции распределения: Если подставить равномерно распределенную величину в обратную функцию распределения, то получится случайная величина с заданным распределением.

    Пусть x искомая случайная величина Fx(t) = P(x < t). u -равномерно распределенная случайная величина. Тогда x = Fx^(-1)(u).

    Например, для экспоненциальной случайной величины Fx(t) = 1-e^(-lt). Обратная функция будет Fx^(-1)(y) = -ln(1- y)/l. Значит считаете ваше случайное число, делите на MAX_RAND, подставляете в формулу -ln(1-y)/l. Или можно упрастить и брать просто -(ln y)/l, потому что равномерная случайная величина от 0 до 1 симметрична.

    Проблема тут с тем, что не для всех распределений можно получить обратную функцию в виде формулы. Для нормального распределения формулы как выше нет - надо использовать функцию erf(), или считать ее приблеженно руками через какие-нибудь ряды.
    Ответ написан
    Комментировать
  • Почему неправильно работает сортировка вставками из "Алгоритмов..." Кормена и др.?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Потому что в книге нумерация элементов идет с 1, а в питоне - с 0.

    Надо поменять условия в циклах for и while.
    Ответ написан
  • Как составить наиболее эффективный алгоритм групповой рассылки сообщений по каналам WebSocket?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вам по-любому придется отправить сообщение каждому пользователю, кто онлайн и подписан на канал.

    Единственное, что вы можете соптимизировать - это нахождение списка пользователей.

    Вам надо как-то побыстрее определить, какие пользователи сейчас онлайн из данного канала. Можно или поддерживать эту информацию в памяти (это будет map из channel_id в set user_id). Когда пользователь выходит в онлайн, надо добавить его в структуру данных для всех каналов, на которые он подписан. Когда пользователь отваливается - удаляйте его из памяти и чистите структуру (надо удалить ключ из map, если там значение осталось пустым).

    Еще вариант: сделать это прямо в базе данных. Для каждого пользователя поддерживайте флаг - онлайн ли он. И в базе данных спрашивайте список всех онлайн пользователей, которые подписаны на нужный канал. Можно даже ваш map user_id->socket_channel_id в базу запихать.
    Ответ написан
    Комментировать
  • Бинарный поиск. Правильно ли работает?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    if i == middle - 1 or i >= 5 and len(list) <= 4:
                return -1


    Вот эта часть вызвает вопросы. Отладочный код остался?

    Проблема в вашем коде, что он виснет, если числа в массиве вообще нет. Условие должно быть не про равенство, а про то, что у вас отрезок пуст (или длины 1) - надо сравнивать first и last.

    Обычно middle не поддерживают между итерациями цикла, а просто считают внутри. Между итерациями переходят только first и last.

    А так, да, бинпоиск - это весьма простой алгоритм.
    Ответ написан
    1 комментарий
  • Дейкстра почти за линейное время (для неориентированного графа)?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    В сетевом роутинге, в основном, нет* центрального узла с информацией о всей топологии, чтобы искать пути. Так что там вообще не работает Дейкстра, а работают распределенные алгоритмы.

    А так, да, на очень специфичных гарфах всеядный алгоритм Дейкстры не самый лучший вариант. И ваш алгоритм тут получается быстрее.

    * Вообще, есть такая тема, как Software Defined Networking, где как раз есть центральный узел, который все и решает, но это чисто теория пока, и там суть не в поиске кратчайших путей в графе а во всяких сложных механизмах приоритезации трафика и устойчивости к сбоям.
    Ответ написан
    1 комментарий
  • Как набрать нужную сумму из определенного количества монет?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Можно целочисленным линейным программированием решать. Бинплиском перебтрайте ответ (k), потом составляйте задачу, где у вас переменные: сколько монет каждого типа берём в каждый из k разменов. И еще будут неравенства, что каждый тип монет используется суммарно не больше, чем есть монет. Целевая функция - любая.

    Еще можно чуть по-другому: находите переблром все варианты одного размена. Потом у вас переменные будут, сколько раз каждый из вариантов берете. Ограничения на общий расход каждой монеты, а целевая функция - сумма переменных. Тут нет бинплиска, но тоже ЦЛП.
    Ответ написан
    Комментировать
  • Доработка алгоритмической задачи JAVA. Требуется помощь >?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вы не ищите 2 максимума, а перебирайте все пары заборов, считайте площадь и выбирайте лучший вариант. Это точно будет работать, хоть и за квадрат.

    Есть более хитрое решение за линию. Вот два самых крайних забора, они могут быть вариантом ответа. В противном случае можно выкинуть из рассмотрения кротчайший из них: если он и есть в максимальном ответе, то можно сдвинуть вторую границу на другой крайний отрезок и ответ не ухудшится.

    Поэтому решение будет из двух указателей - левого и правого. Пока они не сошлись, считайте площадь, сравнивайте ее с текущим ответом, а потом двигайте указатель на меньшее число в сторону второго.
    Ответ написан
    8 комментариев
  • Как вывести кратчайший путь в графе?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вам надо в вашем алгоритме еще завести словарь, куда вы для каждой вершины будете складывать предыдущую в пути. Там, где вы соседнюю вершину кладете в очередь - там надо сарзу смотреть, посещенная она или нет и класть только если нет. Вот в этот момент вы знаете, что вы пришли в graph[city][i] из city.

    В конце циклом по этому массиву пердыдущих вершин пройдитесь и так получите путь в обратном порядке.
    Ответ написан
  • Алгоритмы и структуры данных. Сложность алгоритмов?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Потому что это 2 разных графика, 2 разных примера. Могли бы на втором обозвать вместо f и g f` и g`. Но авторам, наверно, было лень.
    Ответ написан
    1 комментарий
  • Как проходить список и одновременно удалять элементы, в том числе впереди курсора?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Для такой вот работы идеально подходят связные списки. Там можно менять объект не портя никакие итераторы. Проблема только в том, что связные списки в питоне не реализованы на уровне языка. Поищите реализацию, наверняка есть удобный модуль со списками.

    Еще можноиспользовать обычный list, только вы итерируйтесь циклом while и используйте индексы:
    i = 0
    while i < len(arr):
      x = arr[i]
      j = i+1
      while j < len(arr):
        if ShouldDelete(arr[j], arr[i]):
          del a[j]


    Это будет в n раз медленнее, к сожалению. Можно наверно слайсами сделать быстрее:

    arr[i+1:] = y for y in arr[i+1:] if not ShouldDelete(x, arr[i])


    Я не питонист и возможно ошибку допустил. И мне этот код не кажктся очень понятным, но возможно это истиный путь для питона.

    А так, в общем случае, идет двойная итерация с пометками элементов на удаление.

    Какого-то названия этот прием не имеет, ибо это костыль для конкретных реализаций контейнеров.
    Ответ написан
    Комментировать
  • Как транспонировать биты числа максимально быстро?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Занумеруйте все биты от 0 до 16. Потом транспонируйте эту матрицу. Потом посмотрите на разность результата и конца. Вот эти вот числа - это на сколько бит надо сдвинуть исходные биты, чтобы они встали на нужное место.
    0 1 2 3
    4 5 6 7
    8 9 10 11
    12 13 14 15
    
    0 4 8 12
    1 5 9 13
    2 6 10 13
    3 7 11 15
    
    0 -3 -6 -9
    3 0 -3 -6
    ...


    Все биты с одинаковым смещением можно подсчитать за 3 операции: Сдвиг, битовая маска и побитовое или в ответ.
    Я тут вижу 7 разных чисел -9,-6,-3,0,3,6,9.
    Например, для 0 и 3 у вас будет
    answer = (source & 0x1248) | ((source << 3) | 0x2480) | ...


    Это не 8 пока еще операций, а аж 20. Возможно можно как-то еще их сгруппировать.

    Edit: Возможно, еще подход с таблицей будет быстрее. Для каждого из 16 возможных значений строк выдавайте битовое число - столбец, где эти 4 бита на позициях 0, 4, 8,12. Тогда ответ будет table[source&0xf0] | (table[(source>>4)&0xf] << 1) | (table[(source>>8)&0xf] << 2) | (table[(source>>12)&0xf] << 3).

    Тут 13 битовых операций и 4 чтения из памяти.
    Ответ написан
    2 комментария
  • По какому принципу работает алгоритм с массивом очереди?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Много разных вариантов для структур данных. Например, кольцевой буфер. Или связный список. В этом случае можно удалять элемент с начала, не сдвигая все элементы в массиве.
    Ответ написан
  • Проверка на достижимость в направленном графе?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Под конденсацией вы подразумеваете сжатие компонент сильной связности? Ну так у вас остается в конце какой-то произвольный DAG, а не дерево.

    В нем уже нельзя ввести поняие LCA вообще. Например, граф 0->2, 0->3, 1->2, 1->3. Тут для вершин 2 и 3 есть два общих предка: 0 и 1. И какой из них самый нижний?
    Ответ написан
  • Как называется такая алгоритмическая задача?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Задача о покрытии, наверное
    Ответ написан
    Комментировать
  • Как называется это свойство набора кодов (словаря)?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Нету у этого свойства никакого имени. Вы его слишком обобщили. Свойство - определенные символы не могут идти в начале слова. Вот и все.
    Ответ написан
    Комментировать
  • Как устроено окто дерево? Как происходит отсечение видимых грайней?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вернуть все данные в узле. это же самое что дальше продолжать обходить все внутренние рекурсией или стеком узлы, и составить список. В чем профит то тогда? Как можно вернуть все данные по щелчку.


    Да, именно так, надо обойти все узлы дальне.
    Но при этом уже не надо проверять на видимость и код там попроще.

    А профит тут в том, что вы вот так вот обходите не все дерево. а только маленькую его часть.

    Окто дерево используется, чтобы отсечь те объекты, которые сзади или сбоку от камеры и точно не видны. Оно не помогает отсекать объекты, закрытые стеной. Тут, действительно, используется z-buffer.
    Ответ написан
    4 комментария
  • Как разбить треугольник в прямоугольной области на N треугольников?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    mayton2019 правильно вам сказал: сначала пересеките треугольник и ваш прямоугольник. А потом триангулируйте.

    При пересечении у вас 2 выпуклых многоугольника. Алгоритм Сазерленда-Коэна тут немного перебор, ибо там один из многоугольников может быть не выпуклым. В вашем фиксированном случае скорее всего нет даже смысла заморачиваться с O(n log n) алгоритмом. Просто научитесь пересекать любые 2 отрезка. После чего пересеките каждую сторону треугольника с каждой стороной квадрата. Не надо считать за пересечение, если 2 отрезка параллельны и накладываются. Получите макимум 12 точек. К ним добавьте 3+4=7 вершины ваших многоугольников. Потом каждую точку проверьте на принадлежность обоим фигурам, и оставьте только те, которые лежат внутри или на границе. Потом постройте выпуклую оболочку из них (погуглите, эта-то задача вообще везде расписана).

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Раз человек легко прокладывает путь, то его можно просто захардкодить.

    Очевидно, что шаг от одной вершины к следующей будет не меньше 1, а значит кратчайший путь - тот, который ходит только горизонтально и вертикально. Если сетка не квадратная, то пусть ровно с 2m более длинных вертикаьных ребер будет мнимальным, потому что путь любую горизонтальнюу линию должен будет пересечь хотя бы в 2 точках.

    Ну и нарисуйте на бумаге 4 случая - когда высота и ширина четные или нечетные.
    Ответ написан
    Комментировать
  • Когда целесообразно использовать именно такую реализацию DSU?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    DSU выполняет две операции: проверить, принадлежат ли 2 элемента одному множеству; объеденить множества двух данных элементов. Обе за O(log*n) ассимтотически. Это не логарифм, а суперлогарифм, или обратная функция Аккермана. Это - сколько нужно двоек сложить в степенную башню, чтобы набрать n. Она растет так медленно, что ее можно считать константой на практике (она достигнет 4 только при n=2^65536 - вы столько числел не сохраните во всех датацентрах мира).

    Я бы в качестве альтернативной, "тривиальной" реализации рассматривал массив пометок + списки в массиве:
    для каждого элемента храним номер его множества, а для каждого номера храним список всех его элементов в списках (так же, как и в DSU, в одном массиве ссылок на следующий элемент).

    Эта структура компактна по памяти и более быстра, чем ваши хеш таблицы. Тут можно за O(1) проверить, что два числа в одном множестве и за O(log n) объеденить два множества (амортизированно, если перекрашиваем меньшее множество).

    Итак, имеем O(Log*n) vs O(1) за проверку и O(log*n) vs O(log n) за объединения.

    Т.е. вроде бы имеет смысл использовать пометки+списки, если у вас заметно больше проверок, чем объединений.

    Но на практике там выигрыша нет, ибо редко когда у вас сильно больше проверок. Да и, если у вас много проверок, то оценка O(log*n) - завышена, ведь если вы одну и ту же проверку повторяете, то там пути сжимаются и проверки работают уже за O(1).

    Таким образом, DSU от Тарьяна - лучше всех других структур на практике.
    Ответ написан
    7 комментариев