Рубрики
Без рубрики

Нежное введение в разрыв и завоевать алгоритмы

Легкий для понимания учебника при создании быстрых алгоритмов. **НУЛЬ* * Знание информатики необходимо. Теги с Python, JavaScript, начинающими, информатики.

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

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

Divide и Conquer – один из способов атаковать проблему под другим углом. На протяжении всей этой статьи я собираюсь поговорить о создании решений для разделения и завоевания и о том, что это такое. Не волнуйтесь, если у вас есть ноль опыт или знания по теме. Эта статья предназначена для чтения кем -то с очень небольшими знаниями по программированию.

Я собираюсь объяснить это, используя 3 примера. Первым будет простое объяснение. Второй будет какой-то код. Финал попадет в математическое ядро разделения и передачи техники. (Не волнуйтесь, я тоже ненавижу математику).

Нет времени читать это? Знак до моего списка электронной почты, чтобы получить это в форме PDF. Вы также получите дополнительный контент, который не в этом посте ✨ Подпишите здесь.

Что такое деление и завоевание? 🌎

Divide and Conquer – это то, где вы делите большую проблему на многие меньшие, гораздо легче решать проблемы. Довольно маленький пример ниже иллюстрирует это.

Мы принимаем уравнение «3 + 6 + 2 + 4», и мы сокращаем его на наименьший набор уравнений, который составляет [3 + 6, 2 + 4]. Это также может быть [2 + 3, 4 + 6]. Заказ не имеет значения, пока мы превращаем это одно длительное уравнение на многие меньшие уравнения.

Допустим, у нас 8 чисел:

И мы хотим добавить их всех вместе. Сначала мы разделите проблему в 8 равных подпроставлениях. Мы делаем это, разбив дополнение на отдельные числа.

Затем мы начинаем добавлять 2 числа одновременно.

Затем 4 числа в 8 чисел, что является нашим результирующим.

Почему мы разбиваем его до отдельных чисел на этапе 1? Почему бы нам не только начать с этапа 2? Потому что, хотя этот список номеров, даже если список был нечетным, вам нужно будет разбить его до отдельных чисел, чтобы лучше справиться с ним.

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

Формально техника, как определено в знаменитом Введение в алгоритмы Корменом, Лейсерсон, Риверст и Штейн:

  1. Разделять

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

  1. Покорять

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

Рекурсия – это когда функция вызывает себя. Это тяжелая концепция, чтобы понять, если вы никогда не слышали об этом раньше. Эта страница обеспечивает хорошее объяснение. Короче говоря, рекурсивная функция такая такая:

n = 6

def recur_factorial(n):
   if n == 1:
       return n
   else:
       return n * recur_factorial(n-1)

print(recur_factorial(n))

Я полностью объясню код за секунду.

  1. Комбинировать

Возьмите решения в подпростоки и объедините их в решение первоначальной проблемы.

С кодом сверху, некоторые важные вещи следует отметить. Часть разделения также является рекурсионной частью. Мы разделяем проблему на Возврат n * recur_factorial (n-1) Анкет

В частности, recur_factorial (n-1) Часть – это то, где мы разделяем проблему.

Завоевающая часть также является рекурсионной частью, но также и заявление IF. Если проблема достаточно мала, мы решаем ее напрямую (возвращая n). Еще, мы выполняем Возврат n * recur_factorial (n-1) Анкет

Комбинировать. Мы делаем это с символом умножения. В конце концов, мы возвращаем фактор числа. Если бы у нас не было символа, и это было return recur_factorial (n-1) Это не будет объединено и не вызовет ничего, что удаленно похоже на фактор. (Это выведет 1, для тех, кто заинтересован).

Мы собираемся исследовать, как работает Divide и Conquer в некоторых известных алгоритмах, сортировке слияния и решении башни Ханоя.

Сортировка слияния 🤖.

Сортировка слияния – это сортировочный алгоритм. Алгоритм работает следующим образом:

  • Разделите последовательность N чисел на 2 половины
  • Рекурсивно сортировать две половинки
  • Объединить две сортированные половины в одну отсортированную последовательность

На этом изображении мы разбиваем 8 номеров в отдельные цифры. Так же, как мы это сделали раньше. Как только мы сделаем это, мы можем начать процесс сортировки.

Это сравнивает 51 и 13. С 13 меньше, он ставит его в левую сторону. Это делает это для (10, 64), (34, 5), (32, 21).

Затем он сливается (13, 51) с (10, 64). Он знает, что 13 – самый маленький в первом списке, а 10 – самый маленький в правильном списке. 10 меньше 13, поэтому нам не нужно сравнивать от 13 до 64. Мы сравниваем и объединяем два отсортировано списки

В рекурсии мы используем термин базовый корпус Чтобы ссылаться на абсолютно наименьшее значение, с которым мы можем иметь дело. С сортировкой слияния базовый корпус составляет 1. Это означает, что мы разделяем список, пока не получим подразделения по длине 1. Вот почему мы идем до 1 и не 2. Если бы базовый случай был 2, мы бы остановились на 2 номерах.

Если длина списка (n) больше 1, то мы делим список и каждый суб-списк на 2, пока не получим сублисты размера 1. Если список уже отсортирован, поэтому мы ничего не делаем.

Сортировка слияния является примером алгоритма деления и завоевания. Давайте посмотрим на еще один алгоритм, чтобы по -настоящему понять, как работает Divide и Conquer.

Башни ханой 🗼

Башни Ханой является математической задачей, которая состоит из 3 колышек и в этом случае 3 диска. Эта проблема в основном используется для обучения рекурсии, но у нее есть некоторые Реальный мир использует.

Каждый диск – это другой размер. Мы хотим переместить все диски в PEG C, чтобы самая большая находятся на дне, второй по величине на вершине крупнейшего, третьей по величине (наименьшего) на вершине всех из всех них. Есть несколько правил к этой игре:

  1. Мы можем переместить только 1 диск за раз.
  2. Диск не может быть размещен на других дисках, которые меньше его.

Мы хотим использовать наименьшее количество движений. Если у нас есть 1 диск, нам нужно только переместить его один раз. Если у нас есть 2 диска, нам нужно переместить его 3 раза.

Количество движений составляет 2 минус 1. Если у нас есть 4 диска, мы рассчитаем минимальное количество ходов как -.

Чтобы решить вышеприведенный пример, мы хотим хранить наименьший диск в буфере PEG (1 перемещение). См. Ниже для GIF на решении башни Ханоя с 3 колышками и 3 дисками.

Обратите внимание, как нам нужно иметь буфер для хранения дисков.

Мы можем обобщить эту проблему. Если у нас есть n дисков: перемещать N-1 от A в B в B рекурсивно, перемещайте наибольшее от A в C, перемещайте N-1 от B в C в C рекурсивно.

Если есть равномерное количество кусочков, первое движение всегда находится в середине. Если есть нечетное количество кусочков, первое движение всегда на другое конец.

Давайте начнем кодировать алгоритм для TOH, в псевдокоде.

function MoveTower(disk, source, dest, spare):
    if disk == 0, then:
        move disk from source to dest

Начнем с базового корпуса, диск . Источник это колышка, с которой вы начинаете. dest это конечный пункт назначения. запасной это запасной колышек.

FUNCTION MoveTower(disk, source, dest, spare):
IF disk == 0, THEN:
    move disk from source to dest
ELSE:
    MoveTower(disk - 1, source, spare, dest) // Step 1
    move disk from source to dest // Step 2
    MoveTower(disk - 1, spare, dest, source) // Step 3
END IF

Обратите внимание, что с шагом 1 мы переключаем dest и источник . Мы не делаем этого для шага 3.

С рекурсией мы можем быть уверены в 2 вещи:

  1. У него всегда есть базовый чехол (если это не так, как алгоритм должен закончить?)
  2. Функция вызывает себя.

Алгоритм становится немного запутанным с шагами 1 и 3. Они оба называют одну и ту же функцию. Вот где появляется многопоточное. Вы можете запустить шаги 1 и 3 на разных потоках – одновременно.

Поскольку 2 – более 1, мы снова перемещаем его на один уровень. Пока что вы видели, что такое метод деления и завоевания. Вы должны понимать, как это работает и какой код выглядит. Далее давайте узнаем, как формально определить алгоритм для проблемы с использованием Divide и Conquer. Эта часть является наиболее важным на мой взгляд. Как только вы это знаете, он будет проще для создания алгоритмов деления и завоевания.

Fibonacci числа 🐰

Числа Фибоначчи можно найти в природе. Путь Кролики производят находится в стиле чисел Фибоначчи. У вас есть 2 кролика, которые делают 3, 3 кролика делают 5, 5 кроликов делают 9 и так далее.

Числа начинаются с 1, и следующий номер – текущий номер + предыдущий номер. Вот 1 +. Затем 1 +. 2 + и так далее.

Мы можем описать эту связь, используя рекурсию. Рецидив – это уравнение, которое определяет функцию с точки зрения его меньших входов. Рецидив и рекурсия звучат одинаково и похожи.

С числами Фибоначчи, если или 1, это приводит к 1. Иначе рекурсивно добавьте f (n -1) + f (n -2), пока не достигнете базового корпуса. Давайте начнем с создания нерекурсивного калькулятора числа Фибоначчи.

Мы знаем, что если или 1, верните 1.

def f(n):
    if n == 0 or n == 1:
        return 1

Числа Fibonacci – это последние два числа, которые добавлены вместе.

def f(n):
    if n == 0 or n == 1:
        return 1
    else:
    fibo = 1
    fibroPrev = 1
    for i in range (2, n):
        temp = fibo
        fibo = fibo + fiboPrev
        fiboPrev = temp
        return fibo

Теперь мы видели это, давайте перевернем его в рекурсию, используя рецидивов.

При создании рецидива мы всегда начинаем с базового случая. Базовый случай вот если n или 1, возврат n.

Если мы не вернемся, но вместо этого возврату 1 это приводит к ошибке. Например, F (0) приведет к 1. Когда на самом деле это должно привести к 0.

Далее у нас есть формула. Если n не 0 или 1, что мы делаем? Мы рассчитываем f (n – 1) + f (n – 2). В конце концов, мы хотим объединить все цифры вместе, чтобы получить наш окончательный результат. Мы делаем это с помощью добавления.

Это формальное определение чисел Фибоначчи. Обычно рецидивы используются, чтобы говорить о времени работы алгоритма разрыва и завоевания. Мой профессор алгоритмов и я думаю, что это на самом деле хороший инструмент для создания алгоритмов разделения и завоевания.

def F(n):
  if n == 0 or n == 1:
    return n
  else:
    return F(n-1)+F(n-2)

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

Мы часто рассчитываем результат рецидива с использованием дерева выполнения. Компьютерные Повелители 🤖 Не нужно это делать, но для людей полезно увидеть, как работает ваш алгоритм разрыва и завоевания. Для f (4) это выглядит как:

n 4, а n больше 0 или 1. Итак, мы делаем F (N-1) + F (N-2). Мы игнорируем дополнение на данный момент. Это приводит к 2 новым узлам, 3 и 2. 3 больше 0 или 1, поэтому мы делаем то же самое. То же самое для 2. Мы делаем это, пока мы не получим кучу узлов, которые либо 0, либо 1. Затем мы добавляем все узлы вместе. 1 + 1 + 0 + 0 +, который является правильным ответом.

Заключение 📕

После того, как вы определили, как сломать задачу на множество небольших частей, вы можете использовать одновременное программирование, чтобы одновременно выполнить эти кусочки (на разных Threads ), тем самым ускоряя весь алгоритм.

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

  • Что такое деление и завоевание?
  • Рекурсия
  • Сортировка слиянием
  • Башни Ханой
  • Кодирование алгоритма разделения и завоевания
  • Рецидивы
  • Fibonacci числа

Следующим шагом является изучение многопоточного чтения. Выберите язык выбора программирования и Google, в качестве примера «Python Multiethreading». Выясните, как это работает, и посмотрите, сможете ли вы атаковать какие -либо проблемы в своем собственном коде с этого нового угла.

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

Подписаться здесь

Оригинал: “https://dev.to/brandonskerritt/a-gentle-introduction-to-divide-and-conquer-algorithms-1ga”