Задать вопрос
Ответы пользователя по тегу Алгоритмы
  • Какой можно применить алгоритм для хранение индекса для 50 миллиардов записей в golang?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    vgray, Удаляются ли записи?

    Так-то, поиск на полное совпадение по строке не самое сложное. Какое-нибудь B-Tree или Trie - отлично подойдут тут. Проблема только в том, что оно в память не влезает, поэтому придется повозиться с хранением этого всего кусками в файлах. B-Tree как раз для этого и сделано, но оно будет медленнее работать со строками. Аккуратно порезанный на куски Trie будет быстрее.
    Ответ написан
    Комментировать
  • Как найти площадь квадрата, имея 2 отрезка?

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

    Не совсем пока еще понятно, квадрат может быть ориентирован как угодно, или тоже должен быть параллелен осям координат? Судя по тупости формулеровок я думаю, что составители задачи имели в виду более простой вариант, и стороны квадрата тоже должны быть параллельны.

    В этом случае можно сначала построить обрамляющий прямоугольник вокруг обоих прямоугольников (взять максимум и минимум по каждой оси из всех углов). А потом взять его большую сторону в качестве стороны квадрата-ответа (возвести в квадрат и вывести).

    Если же это не сработает и квадрат можно вращать, то тут все сложно. Путем очень хитрых геометрических рассуждений можно доказать, что квадрат в оптимальном случае, все-таки, будет параллелен осям координат и просто написать решение выше. Ну, или, если не верите, то можно ввести угол поворота как переменную, написать функцию, котороя поворачивает все точки на данный угол, а потом ищет (по алгоритму в начале) площадь описанного параллельного осям квадрата, и потом запустить тернарный поиск минимума по этой функции.
    Ответ написан
    2 комментария
  • Как однозначно конвертировать цвет из RGB в HSL и обратно, получая один и тот же результат?

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

    Похоже там тупо где-то стоит округление вниз, ведь разница после преобразований по всем каналам - ровно 1. Может, это значение RGB в HSL на самом деле что-то вроде (15.01, 30.5, 49.9). Но в пиксели HSL в итоге запишутся целые числа (0-255). Из-за этого округления при обратном преобразовании получается не 0x91 а 0x90.6, что, опять же, округляется вниз.

    Если так важна полная обратимость преобразований, попробуйте использовать 10-битные или 16-битные форматы пикселей (расширяя исходный 8-битный RGB нулями справа). Тогда эти округления не испортят важные вам 8 старших бит.
    Ответ написан
    1 комментарий
  • Как найти похожие отрезки в 2 множествах данных, или корреляцию с смещением?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Формализуйте метрику корелляции. Там если расписать - то в итоге все придет к свертке (если одну последовательность развернуть сначала). Ее можно за n log n подсчитать быстрым преобразованием фурье.
    Ответ написан
    Комментировать
  • Kак показать на экране все числа до заданного числа, которые будут кратны второму заданному числу через цикл (while)?

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

    Это реально 3 строки кода, если вам непонятно, снова почитайте учебник про циклы.
    Ответ написан
    Комментировать
  • Как перевести строку в число в ассемблере?

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

    Надо уметь только делить с остатком и умножать на 10. Как перевести 1234 в строку? Можно взять последнюю цифру - осток от деления на 10. Вот вы получили цифру 4. В строке это будет символ "4", или байт со значением 0x34. Вообще, для получения символа по цифре - надо прибавить 0x30. Это мы взяли остаток, а вот результат деления - 123. Можно продолжить перевод так же и мы получим символы в обратном порядке.

    Итак, пока число не 0, делим нацело на 10. Остаток приписываем в ответ переводя в символ. В конце разворачиваем строку.

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

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

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

    Второй вариант - выходные дни недели подсчитать ручками: если дата начала и дата коца в одной неделе, то выходных нет. Иначе, если начало не понедельник - то сдвинуть ее до следующего понедельника и прибавить следующие субботу и воскресение к счетчику. Потом, если дата конца не пятница - сдвинуть ее на предудущий понедельник. Теперь разность между датами кратна 7 и покрывает сколько-то недель целиком. Делите разность на 7 и умножайте на 2, чтобы подсчитать, сколько там суббот и воскресений (разность может быть и 0 и это нормально).

    Потом надо ручками же подсчитать все праздники. Для этого составьте в массиве даты всех календарных праздников в затронутых годах отсортированные. Потом для каждой задачи в этом массиве бинпоиском найдите первый и последний праздник в отрезке. Вот вы уже знаете их количество (разность индексов +1). Так же можно еще вести список всех рабочих суббот и воскресений. Этот список дат можно, наверно, где-то из интернета автоматически скачивать, а можно и тупо ручками в январе вбивать в базу.
    Ответ написан
    Комментировать
  • Почему один код выполняется быстрее другого C++?

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

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

    Поскольку второй код отбрасывает все копейки при прибавлении, то ему требуется больше итераций, чтобы дойти до y. На самом деле он может даже виснуть, если там получается прибавление 0.
    Ответ написан
    1 комментарий
  • Как с MathNet.Numerics уменьшить число коэффициентов у преобразования Фурье?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Быстрое преобразование Фурье - это алгоритм вычисления дискретного преобразования фурье. От интеграллов на картинках оно отличается тем, что там не функция от которой береться интеграл, а массив данных, которые как-то суммируются. Оно называется быстрым, потому что там идет вычисление не в лоб по формуле из википедии, а чуть хитрее, за счет чего оно работает быстрее.

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

    И да, там результат - это массив комплексных чисел, поэтому выходные данные в 2 раза больше входных, которые являются вещественными.
    Ответ написан
    3 комментария
  • Какова сложность алгоритма?

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

    Решение со стеком будет линейным.
    Ответ написан
    Комментировать
  • Как получить все точки в промежутке координат XY1;XY2?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Проще "отсортировать" заданные координаты. Если x1 > x2, поменяйте их местами. То же с z1 и z2, А потом остается только первый случай в вашем коде.

    Это сильно короче писать и легче читать и понимать. В ваших четырех копипастах очень легко где-то допустить ошибку и очень тяжело ее потом найти.
    Ответ написан
    Комментировать
  • Что значит, что алгоритм работает...?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Константа вылезает из деталей реализации операций. Вот сортировка пузырьком делает ровно n(n-1)/2 сравнения и помен в худшем случае. Еще столько же операций инкремента счетчика индекса, столько же проверок на конец цикла. Плюс еще n операций для внешнего цикла. И все это O(n^2). Хотя там есть есть /2 и операций четыре типа. И они еще разное время занимают. Скажем, проверки занимают 10 тактов, а инкрименты - 1. Но вся эта мишура не влияет на скорость роста и прячется в константе. Если все сложить. То будет C*n^2 +C2*n+C3 тактов на алгоритм.
    Ответ написан
    Комментировать
  • Как описать алгоритм для работы с очередями?

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

    Если же числа небольшие, то для двух касс еще есть решение через динамическое программирование, но его сложно обобщить на большее количество касc.
    Ответ написан
    Комментировать
  • Как правильно пропарсить лабиринт в граф?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Самое простое - это сделать вершиной каждую клетку и гнать в этом графе поиск в ширину. Можно даже не строить граф явно - а просто при обходе смотреть на четырех соседей и проверять, а не стена ли там, а были ли мы в клетке (x+dx[k], y+dy[k]), и класть координаты в очередь, если надо.

    Вы же хотите считать только перекрестки и повороты врешинами. В худшем случае, если поворотов в лабиринте много - это будет даже медленнее обхода в ширину. Потому что он работает за линию, а дейкстра - за O(n log n) в лучшем случае. Плюс там константа больше у этого n log n. Правда, это можно решить если вместо дейкстры использовать обобщенный 0-1-BFS (BFS с ограниченными ребрами - там где много очередей используется). Но реализация будет сильно посложнее простого BFS.

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

    Что-то типа такого:
    int last_node = 0;
    for (i = x_start, j = y_start; i <n && j < m; i+= dx, j+=dy) {
       if (is_wall[i][j]) {
        last_node = -1;
      }  
      if (node_number[i][j] <- 0) continue;
      if (last_node > 0) {
        // добавить ребро между вершинами.
        AddEdge(last_node, node_number[i][j]);
      }
      last_node = node_number[i][j];
    }


    Тут я просто добавляю ребра в строке или столбце между двумя соседними вершинами.
    Ответ написан
    Комментировать
  • Алгоритм максимально равномерного распределения предметов?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    std::vector<int> DistributeExtraObjectsFairly(std::vector<int> objects, int extra_objects) {
        std::vector<int> permutation;
        permutation.reserve(objects.size()+1);
        permutation.resize(objects.size());
        std::iota(permutation.begin(), permutation.end(), 0);
        std::sort(permutation.begin(), permutation.end(),
            [&objects](const int &a, const int &b) {return objects[a] < objects[b];});
        int num_increased = 1;
        permutation.push_back(0);
        int step = (objects[permutation[num_increased]] - objects[permutation[num_increased-1]]) * num_increased;
        while (extra_objects >= step && num_increased < objects.size()) {
            extra_objects -= step;
            ++num_increased;
            step = (objects[permutation[num_increased]] - objects[permutation[num_increased-1]]) * num_increased; 
        }
        int final_value = objects[permutation[num_increased-1]] + extra_objects / num_increased;
        int num_bigger_values = extra_objects % num_increased;
        for (int i = 0; i < num_bigger_values; ++i) {
            objects[permutation[i]] = final_value + 1;
        }
        for (int i = num_bigger_values; i < num_increased; ++i) {
            objects[permutation[i]] = final_value;
        }
        return objects;
    }


    Идея: отсортировать числа и добавлять к минимальному, пока оно не станет равно следующему. Потом добавлять к двум мнимальным. Потом к трем и т.д. Вместо добавления к каждому числу индивидуально по одному объекту считаем, сколько надо объектов чтобы первые несколько первых стали равны следующему. Когда нашли, что дальше не приравнять - распределяем остаток среди затронутых элементов по-ровну, остатки кидая куда-то.

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

    Edit: работает за O(n log n), где n - количество элементов в списке. Быстрее, я подозреваю, никак. Есть еще решение за O(n log M) - где М - максимально возможное число во входных данных. Это через бинпоиск по минимальному числу в конце.
    Ответ написан
    Комментировать
  • Путь коня или какова польза ограничений?

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

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

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

    Смотрите на ограничания - n и m до 100000. Обычно такой порядок чисел означает, что вам нужно решение за O(n+m) или что-то вроде O(n+m log (n+m)). У вас же решение "в лоб" за O(nm), что никак не ускорить принципиально. Даже переписыванием на си со всякими хитростями вы ускорите его в 10, ну в 100 раз. А вам надо ускорить его в 10000 раз.

    Могу дать подсказки:
    Во-первых, если и там и там есть положительные элементы, то взяв максимальные в двух массивах вы точно получите элемент больше всех a и b. То же, если и там и там есть отрицательные элементы - надо взять два минимальных.

    Остался случай - что если один массив целиком отрицателен, а второй - целиком положителен. Пусть a положителен, а b отрицателен. Что будет, если взять минимальные по модулю элементы и там и там? Посмотрите внимательно - вам дано, что ни одно из чисел не 0. Это поможет.
    Ответ написан
  • Можете решать эту задачу?

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

    Воспользуйтесь алгоритмом тарьяна: https://e-maxx.ru/algo/lca_linear_offline для получения ответов на все запросы LCA сразу.

    Сначала при вводе создайте дерево целиком и запомните все запросы Get в массив. Потом пройдитесь по массиву запросов и раскидайте их в списки на вершинах, как надо в алгоритме таръяна.

    Вам надо лишь чуть-чуть подкорректировать этот код, чтобы вместе с вершиной-запросом добавлять в списки к вершинам номер этого запроса. И тогда вместо вывода v+1, q[v][i]+1 вы сможете записать ответ в массив по нужному индексу.

    Ну или воспользуйтесь онлайн алгоритмом для поиска LCA через обход со временем входа/выхода и деревом отрезков на минимимум.
    Ответ написан
    Комментировать
  • Как можно ускорить алгоритм?

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

    Проблема в том, что если число K простое - то вы будете проходиться до него. Есть трюк - достаточно проверять только числа до корня из K. Ведь если у числа есть какой-то минимальный собственный (меньше него самого) делитель, то он точно меньше корня (потому что делителей как минимум два, и если минимальный из них больше корня, то их произведение - больше самого числа).

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