Задать вопрос
  • Как изменить обычную сортировку вставками так, чтобы алгоритм использовал бинарный поиск?

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

    Приведенная вами реализация - довольно быстрая. Тут поиск места вставки и сдвиги всех элементов объединены. Теоретически, можно сначала сделать бинарный поиск и вместо цикла while использовать, допустим, цикл for с фиксированным количеством итераций. Это сократит количество сравнений в цикле 2 раза, но добавит сколько-то сравнений и нелокальных обращений к памяти в бин.поиске. Возможно будет работать быстрее только если входные данные почти в обратном порядке заданы. В среднем же, будет работать хуже.

    Гораздо лучшая реализация была бы изначально зарезервировать первый элемент в массиве и записать туда очень маленькое число. А сами данные записывать после него. Тогда в цикле while не надо будет сравнивать index>0. Эта реализация будет гораздо быстрее дополнительного бинарного поиска.
    Ответ написан
    2 комментария
  • Как правильно реализовать поворот и движение фигуры?

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

    dx = s*cos(d);
    dy = s*sin(d);

    Обычно, аргументы библиотечных функций cos и sin задаются в радианах. Так что, если у вас d в градусах -то его надо домножить на Pi/180.0

    Эти же синусы и косинусы нужно использовать для отрисовки треугольника. Если, координаты центра x0, y0 - то координаты вершины-острия будут
    x0+triangle_size*cos(d);
    y0+triangle_size*sin(d);

    Две другие вершины строятся точно так же, но, для вытянутой формы нужна другая константа множитель и углы там будут d+120.0 и d-120.0 (разные углы дадут разную форму треугольника).

    Соответственно, алгоритм - при нажатии кнопки поворота увеличиваете или уменьшаете d. При нажатии кнопки движения сдвигаете координаты треугольника на (dx, dy).

    Более продвинутые и быстрые решения используют матрицы поворота. Но проще всего это будет понять так - вы не считаете cos(d) и sin(d) каждый раз. А храните их значения. При повороте на угол alpha вам надо будет подсчитать новые значения, используя стандартные тригонометрические формулы из старых значений:

    cos(d+alpha) = cos(d)*cos(alpha) - sin(d)*sin(alpha);
    sin(d+alpha) = cos(d)*sin(alpha) + sin(d)*cos(alpha);

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    1) В коде ошибка - два последних if-а, с рекурсивными вызовами, должны быть за while (i <= j), а не в нем.

    Этот while-цикл - совершает разбиение элементов так, что элементы в s..j - меньше pivot, а в i..f - больше.
    Поддерживается инвариант, что слева от i - только элементы меньше-равные pivot, а справа от j - больше-равные.
    Мы вообще игнорируем, где в массиве pivot, берем только его значение. Неважно в центре он, или вообще первый элемент в массиве.

    Сначала в цикле двигаем i до упора, пропуская меньшие элементы. Обратите внимание - сравнение строгое. По инварианту, правее j - элементы большие-равные pivot, поэтому цикл остановится всегда, если было хоть раз сдвинуто на предыдущей итерации внешнего цикла while. В противном случае - это первая итерация - и где-то в массиве есть сам pivot элемент - на нем цикл точно остановится.

    Потом делаем то же самое но с другой стороны, уменьшая j. Теперь у нас следующая ситуация: arr[s..i-1
    ]<=pivot, arr[i]>=pivot, ???, arr[j]<=pivot, arr[j+1..f]>=pivot. После помены i-того и j-того элементов местами, мы можем сдвинуть i и j на еще одну позицию - ведь теперь новый arr[i]<=pivot, что мы и должны поддерживать в инварианте.

    2 крайних случая: если i==j, то, очевидно, arr[i]==arr[j]=pivot, и можно без нарушения инварианта сдвинуть указатели. Если i>j после вложенных while - это говорит о том, что arr[j..i] == pivot, arr[j-1]<=pivot, arr[i+1]>=pivot, и можно не сортировать часть массива между j и i.

    Отвечая на ваш вопрос: Если слева от pivot недостаточно элементов, то i будет указывать на pivot - и при swap-е сдвинет pivot в правый конец, за j. После этого момента между i и j может вообще не быть элемента равного pivot. Но левее i и правее j Будут те элементы, на которых циклы точно остановятся.
    Ответ написан
    Комментировать
  • Структура данных для хранения точек на карте

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Для вашей задачи подойдет и квадродерево: ru.wikipedia.org/wiki/%D0%94%D0%B5%D1%80%D0%B5%D0%...
    Поиск ближайшей точки к заданной выполняется рекурсивным перебором с отсечениями (не стоит рассматривать квадрат, если он целиком дальше, чем имеющийся промежуточный ответ).
    Поиск всех точек, которые надо вывести делается так же рекурсивно.

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Простое, но медленное решение - проверять на пересечение все пары отрезков. Это работает за квадратичную сложность от количества отрезков в контуре.
    Проверка пересечения двух отрезков делается довольно просто. Можно пересечь 2 прямые, заданные параметрически (2 линейных уравнения, решение на бумажке), а потом проверить, что точка пересечения на каждой прямой внутри отрезка (можно задать параметрическое уравнение прямой так, что отрезок соответствует параметрам от 0 до 1). Другой, более быстрый способ с помощью векторного произведения. Надо проверить, что точки каждого отрезка лежат по разные стороны от прямой, на которой лежит другой отрезок.

    Есть сложное, но более быстрое решение задачи - с помощью заметающей прямой. Подробно описанно вот тут: e-maxx.ru/algo/intersecting_segments
    Работает за O(n log n), где n - количество отрезков. Его, правда, придется чуть-чуть подкорректировать, чтобы не учитывать пересечения соседних отрезков.
    Ответ написан
    Комментировать
  • Как найти кратчайший путь в динамическом графе?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Утверждение: Рассмотрим какую-то вершину v. Допустим самый ранний момент, в который можно попасть в нее - t. Если эта вершина есть в кратчайшем пути из вершины a в вершину b, то обязательно есть путь, не длинее, в котором вершина v посещается как раз в момент t.
    Такой путь можно получить конструктивно - начало взять из кратчайшего пути в v, а конец из кратчайшего пути из a в b, может придется подождать какое-то время в вершине, прежде чем продолжить путь.

    С учетом этого утверждения становится понятно, что можно решать вашу задачу стандартным алгоритмом дейкстры для поиска самого быстрого пути во все вершины. Там при релаксации (пересчете минимального растояния) надо просто учитывать, в какое ближайшее к текущему веремни ребро станет доступно для прохождения.
    Ответ написан
    Комментировать
  • Игры на алгоритмы

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Еще интересная игра — SpaceChem. Тут не совсем привычное программирование. Надо на плоском поле расставлять в клетках команды. Некоторый манипулятор двигается по полю, повинуясь командам и берет/отпускает атомы. Надо собирать из атомов определенные молекулы. Развивает не только алгоритмический образ мышления, но и воображение и память.
    Ответ написан
    Комментировать