Ответы пользователя по тегу Алгоритмы
  • K-ая порядковая статистика. В чем проблема?

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

    Представьте, что у вас массив из 2 элементов и первый - больше второго (a[0] = pivot > a[1]).

    До цикла l = 0, h = 2, i = 0. Потом в первом while вы делаете i++. Потом сравниваете a[i] с pivot в первом while. a[1] < pivot по предположению, поэтому вы делаете i = 2. Все - вы уже вышли за границу массива.

    Перепишите со стандартной схемой разбиения Хоара или Ломуто.

    Или придется всякие условия i < j везде дописывать, но я не уверен.
    Ответ написан
    2 комментария
  • Самая быстрая реализация алгоритма Дейкстры на javascript?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Попробуйте переписать с массивами фиксированной длины. Во всех реализациях по вашим ссылкам вершины нумеруются строками и куча массивов типа distance и visited на самом деле являются словарями, или как это в js называется. Это работает сильно медленнее тупого массива, пронумерованного от 0 до n.

    Вам понадобится один словарь для перенумерации вершин в числа. Потом преобразуйте гарф на массив массивов, вместо этого сложного объекта.

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

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

    Можно построить нужный вам порядок только для ациклических графов (без циклов).

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вроде все правильно. Какие ограничения? Может быть переполнение, ведь максимальный ответ n(n-1)/2. Для переполнения int достаточно 65536 чисел в массиве.
    Ответ написан
    4 комментария
  • Как разделить "веса" на кластеры КОРРЕКТНО?

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

    Варианты метрик:

    - Для каждого кластера считается наибольшее расстояние между двумя элементами, и это суммируется по всем кластерам. Можно суммировать квадраты этих расстояний, тогда будут наказываться кластеризации с очень большими кластерами.
    - отношение максимального расстояния между соседними точками в любом кластере и минимального расстояния между кластерами.
    - Это может быть и качественная метрика. Любая кластеризация, где расстояние между соседними точками в кластере меньше расстояния между кластерами считается хорошей. Это частный случай предыдущей метрики, но вам достаточно искать не минимум, а любое значение <1.

    Некоторые метрики имеют смысл только при фиксированном количестве кластеров, как первая.

    Разные метрики дают разные кластеризации и все они в каком-то смысле хорошие. Что именно подходит вам в вашей задаче - можете судить только вы эмпирически.

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

    Многие метрики, если они аддитивны как первая, можно считать динамическим программированием: f(i,k) - значение метрики если мы разбили первые i точек на k кластеров.

    Для других, как для второй придется смешивать дихотомию по ответу и динамическое программирование (бинарный поиск по ответу, далее проверяем, а есть ли разбиение с такой или лучшей метрикой. Внутри динамика - минимально достижимое значение максимума между соседними точками в классе среди первых i при разбиении на k кластеров. При переборе последнего кластера нужно смотреть, чтобы расстояние между ним и соседними не превышало ответа динамики деленного на перебираемый коэффициент).

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

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

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

    Можно свести ее к integer linear programming и решать какой-то из множества существующих библиотек/солверов. Если числа маленькие, то можно или полный перебор или какую-то динамику, типа решения задачи о рюкзаке сделать (тут надо будет набранные суммы во всех приборах взять в параметры).

    Если нужно не обязательно оптимальное решение - а что-то не слишком ужасное, то можно делать жадность, как Adamos предложил.
    Ответ написан
    Комментировать
  • Как возвести decimal в степень с плавающей точкой?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вы что-то напутали.
    1.000001**2**19 тоже возвращает 1.689255227180379. Только что в консоли проверил.

    > costumePow(1.000001, 19)
    1.689255227180379
    > 1.000001**2**19
    1.689255227180379
    Ответ написан
    6 комментариев
  • Как перебрать все возможные комбинации символов?

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

    Нужно или писать рекурсивную функцию, или итеративно дописывать ко всем элементам массива по одному элементу из сделеющего множества. Просто переведите этот код на с++.

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

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    При копировании назад из временного массива b в массив a у вас индексация с ошибкой. Хоть b индексируется с 0, вы обращаетесь к элементам начиная с s.
    Ответ написан
    1 комментарий
  • Как решить данную задачу?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вспомните алгоритм преобразования числа из 10-чной системы в t-ичную - надо делить на t с остатком, пока есть что делить. Остатки - цифры в t-ичной системе счисления (от младших к старшим).

    Так, 323 в десятичной системе, если перевести в 16-ричную будет:
    323 = 16*20+3. Остаток 3, результат 20.
    20 = 16*1+4. Остаток 4, результат 1
    1 = 16*0+1. Остаток 1. результат 0.

    Отсюда получается, что искомое число в 16-ричной системе 143. Проверка 16*16*1+16*4+3 = 256+64+3=323

    Итак, вам надо сделать то же самое, но не в 10-чиной системе, а в k-ичной (ведь переводим не из 10-чной а из k-ичной). Число записано в виде k-ичных цифр в массиве. Это, фактически, длинная арифметика с базой k.

    Для этого надо реализовать деление числа в виде массива цифр на короткое с остатком.
    Это делается со старших разрядов к младшим. Тупо делите каждую цифру с остатком отдельно. Остаток переносите в младший разряд. Последний остаток - это остаток от всего деления. Результаты делений - новые цифры.

    Удобнее хранить цифры от младших к старшим в массиве. Т.е. элемент массива 0 - это единицы. Элемент 1 - это первая степень k. Еще стоит хранить номер последней ненулевой цифры. Смотрите реализацию по ссылке выше.

    Вот пример из условия. Пусть k=666, t=10, число - {2, 179, 113} (в условии цифры от старших к младшим, но мы храним наоборот).

    Делим на 10.

    113/10 = 11 + остаток 3. Переносим 3 в младший разряд (умножив на 666), получаем 3*666+179=2177.
    2177 / 10 = 217 + остаток 7. 7*666+2 = 4664.
    4664 /10 = 466 + остаток 4. Дальше разрядов нет, значит 4 и есть остаток от деления {2, 179, 113} на 10. Результат деления - {466, 217, 11}

    Последний остаток - 4 - это и есть младшая цифра в ответе (он в условии написан - 50241044). Надо продолжать делить результат пока он не окажется 0.

    Повторим для {466, 217, 11}:
    11/10 = 1 + остаток 1. 1*666+217 = 883
    883/10 = 88 + остаток 3. 3*666+446 = 2444
    2444/10 = 244 + остаток 4.
    Опять, последний остаток - это остаток всего деления и следующая цифра в ответе (опять 4).
    Результаты деления - {244, 88, 1}

    И так далее, пока все число не станет 0 (все цифры в массиве 0).
    Ответ написан
    Комментировать
  • Поиск целых корней кубического уравнения по заданным коэффициентам?

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

    Можно проверить в 2 этапа. Подсчитать A=a*x+b, B=c*x+d. Потом проверить что A*x*x = -B. Но тут нужно заранее отсечь случаи, когда abs(A) > abs(B)/(x*x). Тогда переполнений не будет.

    Во-вторых, как же сложно у вас разбор случаев идет. Я не могу разобраться, что там происходит. Там наверняка опечатка, но я даже читать не хочу. Перепишите, пожалуйста. Вам надо найти самый старший ненулевой коэффициент и перебирать его делители. Плюс добавить корень 0, если этот коэффициент не d. Это делается сильно проще так:

    if (d != 0) {
      n = d;
    } else {
      s.push_back(0);
      if (c != 0) {
        n = c;
      } else if (b != 0) {
       n = b;
      } else {
       n = a;
      }
    }
    if (n == 0) {cout << "-1"; return}
    Ответ написан
    2 комментария
  • Как решить задачу с перебором?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Самое простое решение для понимания - полный перебор. Тупо 3 цикла - сколько монет с номиналом 2 взяли, сколько монет с номиналом 3 и сколько монет с номиналом 5. Каждый цикл от 0 до <Сколько осталось набрать> / номинал. Потом проверяем, что сумма сошлась. Более того, можно последний цикл пропустить и просто проверить, что оставшееся можно набрать пятаками.

    bool CanExchange(int n, int have2, int have3, int have5) {
      for(int take2 = 0; take2 <= min(have2, n/2); ++take2) {
        int left2 = n - 2*take2;
        for (int take3 = 0; take3 < min(have3, left2/3); ++take3) {
          int left23 = left2 - take3*3;
          if (left23 % 5 == 0 && left23 / 5 <= have5) {
            return true;
          }
        }
      }
      return false;
    }


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

    Самое быстрое решение - динамическое программирование.

    F(n,k) - можно ли набрать число n первыми k типами монет.

    F(0.0) = 1
    F(*, 0) = 0
    F(n,k) = Sum(i = 0..have[k]) F(n-i*value[k], k-1)

    Фактически, это тот же рекурсивный перебор, только с мемоизацией. Можно переписать это двумя циклами и соптимизировать по памяти до O(n).
    Ответ написан
    Комментировать
  • Что не так в использовании длинной арифметики?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Проблема в выводе. Вы нулевые цифры вообще не выводите:
    for(size_t i=0; i<SIZE; i++) {
      if (n[i]!=0) {
        cout << n[i];
      }
    }


    А нужно пропускать только ведущие нули. Самый простой способ исправить - завести bool printed:
    bool printed = false;
    for(size_t i=0; i<SIZE; i++) {
      if (printed || n[i]!=0) {
        cout << n[i];
        printed = true;
      }
    }


    Ну, еще может быть стоит увеличить размер длинных чисел. Не факт, что 135 цифр хватит. Введите тест n=300, k=300, и проверьте что выводится один и тот же ответ при текущем и увеличенном SIZE.
    Ответ написан
  • В чем может быть ошибка?

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

    Посидите с бумажкой, тут вообще можно формулу для ответа вывести.
    Подсказка: сколько существует i<=j, таких что j-i <= a? Перобразуйте второе неравенство, получите i >= j-a.
    В итоге у вас есть неравенства j-a <= i <= j. Если j >= a+1, то ровно a+1 чисел в этом интервале. Иначе их там j, (потому что левая граница отрицательна или 0, а по условию i >= 1).

    Итого ваш ответ - это сумма от 1 до a, а потом еще n-a слагаемых a. Сумма первой части - арифметическая прогрессия.

    Надо только не забыть рассмотреть случай a>=n-1, тогда ответ тупо количество всех пар.
    Ответ написан
    Комментировать
  • Как исправить данный алгоритм?

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

    Значит, исходя из этого, надо цикл гнать пока start < finish - тут правильно.

    При переходе наверх надо start делать mid+1 - тут правильно.

    При переходе вниз надо finish делать mid - тут ошибка! Ведь только что рассмотренный элемент вы выбрасываете, раз он не подошел. Но предыдущий за ним может быть искомым. Нельзя делать finish = mid-1 - вы теряете потенциально важный элемент.

    Другой вариант исправления - изменить смысл finish быть концом отрезка включительно. Тогда присвоения в цикле правильные, но неверно условие цикла и инициализация. Нужно гнать цекл пока start <= finish и изначально присваивать len(lst)-1.
    Ответ написан
  • Как реализовать такой алгоритм?

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

    Берете случайное число на поле, вокруг которого есть 2 свободных клетки. Растраивайте его на три меньших - убираете случайным образом одно из этих трех чисел. Если в результате на поле остается 3 одинаковых числа рядом, то этот ход был невозможным. Проверять достаточно только последние добавленные 2 числа. Запоминайте, какого типа было число - это и будет "случайным" сгенерированным числом.
    Ответ написан
    Комментировать
  • Как решить эту задачу динамическое программирование?

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

    F(i,j) - максимальное количество монет, которые можно собрать придя в точку (i, j).

    F(0,0) = Z[0,0]
    F(i,j) = Z[i,j] + max(F(i-1,j), F(i,j-1))
    F(-1,*) = F(*,-1) = -100500
    ответ - F(n,m)

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

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    M%p = 0, значит M=k*p для какого-то целого k.

    k*p >= N, значит k >= N/p.

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

    k >= ceil(N/p).

    слева и справа целые числа, надо найти минимальное k, значит k = ceil(N/p).

    Отсюда весь ответ M = p*ceil(N/p)

    ceil(N/p) можно подсчитать в целых числах в C, как (N+p-1)/p

    Еще, если подумать, что вам нужно блищайшее делящееся на p число, то нужно дополнить N%p до p, то можно сделать так: N+(N%p ? p-N%p : 0)
    Ответ написан
    2 комментария
  • Как провалидировать ход коня?

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

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

    Надо вместо сортировки тупо просуммировать все числа и поделить на их количество (округлите вниз).

    Ну и, не забудьте в конце цикл гнать по всем элементам а не от середины.
    Ответ написан
    1 комментарий