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

Жадные алгоритмы для начинающих

Жадные алгоритмы для тех, которые не понимают алгоритмический дизайн. Помечено с компьютером, начинающими, Python, учебником.

Жадные алгоритмы Стремитесь сделать оптимальный выбор в этот момент. Каждый шаг выбирает оптимальный выбор, не зная будущего. Он пытается найти все глобально оптимальный способ решить всю проблему, используя этот метод.

Почему жадные алгоритмы называют жадными?

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

В этот момент вовремя, какой оптимальный выбор сделать?

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

Они никогда не оглядываются назад на то, что они сделали, чтобы увидеть, могут ли они оптимизировать глобально. Это главное различие между жадными и Динамическое программирование Отказ

Для чего используются жадные алгоритмы?

Жадные алгоритмы очень быстро. Намного быстрее, чем две другие альтернативы (разделите и завоевать и динамическое программирование). Они используются, потому что они быстро.

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

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

Как мне создать жадный алгоритм?

Ваш алгоритм должен следовать этой недвижимости:

В этот момент вовремя, какой оптимальный выбор сделать?

И это все. Для этого нет много. Жадные алгоритмы, как правило, легче для кода, чем Разделить и завоевать или Динамическое программирование Отказ

Представьте, что вы торговый автомат. Кто-то дает вам £ 1 и покупает напиток за 0,70р. Там нет монеты 30p в фунт стерлингов Как вы рассчитываете, сколько изменения возврата?

Для справки это деноминация каждой монеты в Великобритании:

1p, 2p, 5p, 10p, 20p, 50p, £1

Жадный алгоритм начинается от наивысшей деноминации и работает назад. Наш алгоритм начинается в £ 1. £ 1 больше 30p, поэтому он не может его использовать. Это делает это на 50p. Достигает 20p. 20p <30p, поэтому требуется 1 20 н.

Алгоритм должен вернуть изменение 10p. Он снова пытается 20p, но 20p> 10p. Затем он идет до 10p. Это выбирает 1 10p, и теперь наша возвращение 0 Мы останавливаем алгоритм.

Мы возвращаем 1x20p и 1x10p.

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

(1p, 10), (2p, 3), (5p, 1), (10p, 0), (20p, 1p), (50p, 19p), (100p, 16)

Алгоритм попросят снова вернуть изменение 30P. 100P (£ 1) нет. То же самое для 50. 20p, мы можем сделать это. Мы выбираем 1x 20p. Теперь нам нужно вернуть 10p. 20p закончился, поэтому мы двигаемся вниз 1.

10P закончился, поэтому мы двигаемся вниз 1.

У нас есть 5p, поэтому мы выбираем 1x5p. Теперь нам нужно вернуть 5p. 5P закончился, поэтому мы двигаемся вниз.

Мы выбираем 1 2P монета. Теперь нам нужно вернуть 3P. Мы выбираем другую монету 2P. Теперь нам нужно вернуть 1P. Мы двигаемся вниз.

Мы выбираем 1x 1P монета.

Всего наш алгоритм выбрал эти монеты, чтобы вернуться как изменение:

# (value, how many we return as change)
(10, 1)
(5, 1)
(2, 2)
(1, 1)

Давайте что-то код. Во-первых, нам нужно определить проблему. Мы начнем с деноминаций.

denominations = [1, 2, 5, 10, 20, 50, 100]
# 100p is £1

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

Если наше Денаминирование Список как указано выше, то [6, 3, 0, 0, 0, 0, 0] Представляет получение монет 6 1P и 3 монетки 2 P, но 0 всех других монет.

denominations = [1, 2, 5, 10, 20, 50, 100]
# 100p is £1

def returnChange(change, denominations):
    toGiveBack = [0] * len(denominations)
    for pos, coin in reversed(list(enumerate(denominations))):

Мы создаем список, размер деноминаций долго и заполнить его 0.

Мы хотим петлю назад, от самой большой до самой маленькой. Обратный (х) Реверс X и позволяет нам петлю назад. Перечислите средства «Для цикла через этот список, но сохраняйте позицию в другой переменной». В нашем примере, когда мы запускаем цикл. монета и POS Отказ

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

denominations = [1, 2, 5, 10, 20, 50, 100]
# 100p is £1

def returnChange(change, denominations):
    # makes a list size of length denominations filled with 0
    toGiveBack = [0] * len(denominations)

    # goes backwards through denominations list
    # and also keeps track of the counter, pos.
    for pos, coin in enumerate(reversed(denominations)):
        # while we can still use coin, use it until we can't
        while coin <= change:

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

denominations = [1, 2, 5, 10, 20, 50, 100]
# 100p is £1

def returnChange(change, denominations):
    # makes a list size of length denominations filled with 0
    toGiveBack = [0] * len(denominations)

    # goes backwards through denominations list
    # and also keeps track of the counter, pos.
    for pos, coin in enumerate(reversed(denominations)):
        # while we can still use coin, use it until we can't
        while coin <= change:
            change = change - coin
            toGiveBack[pos] += 1
    return(toGiveBack)

print(returnChange(30, denominations))
# returns [0, 0, 0, 1, 1, 0, 0]
# 1x 10p, 1x 20p

время выполнения Из этого алгоритма преобладают 2 петли, таким образом, это O (n 2).

Жадный оптимален? Жадные всегда работают?

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

Алгоритм для этого:

  • Выберите 3 доллара монет. 1P, X и менее 2x, но больше, чем x.

Мы выберем 1, 15, 25.

  • Спросите перемен 2 * второй деноминации (15)

Мы попросим перемен 30. Теперь давайте посмотрим, что делает наш жадный алгоритм.

[5, 0, 1]

Он Choses 1x 25p и 5x 1p. Оптимальное решение 2x 15p.

Наш жадный алгоритм не удался, потому что он не смотрел на 15p. Он посмотрел на 25p и подумал “да, это подходит. Давайте возьмем это ».

Затем он посмотрел на 15p и подумал: «Это не подходит, давайте двигаться дальше».

Это пример того, где жадные алгоритмы терпят неудачу.

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

Жадные алгоритмы иногда имеют глобально оптимальные. По сравнению с ранее мы видели эти алгоритмы глобально оптимальными:

Есть и другие глобально оптимальные решения, но жадные быстрее и проще для программирования, чем эти решения.

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

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

Алгоритм следует по этим правилам:

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

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

Мы отмечаем в наших невизимах списка узлов. Расстояние от A до a равно 0. Расстояние от A до B составляет 4. Расстояние от A до C равно 2. Мы обновили наш список расстояний на правой стороне.

Затем мы выбираем самый маленький край, где вершина не была выбрана. Наименьший край a -> c, и мы еще не выбрали C. Мы видим C.

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

Мы можем добраться до B из C. Теперь нам нужно выбрать минимум. Мин (4, 2 +.

Поскольку A -> C -> B меньше, чем A -> B, мы обновляем B с этой информацией. Затем мы добавляем на расстояниях от других узлов, которые мы можем достичь.

Наша следующая самая маленькая вершина с узлом мы еще не посетили, это B, с 3. Мы посещаем B.

Мы делаем то же самое для B. Тогда мы выбираем самую маленькую вершину, которую мы еще не посещали, D.

Мы не обновляем ни одного из дистанций на этот раз. Наш последний узел тогда E.

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

Мы выбираем первый, затем C, затем B. Если вам нужно создать кратчайший путь от A до любого другого узла в качестве графика, вы можете запустить этот алгоритм, используя таблицу с правой стороны.

Используя эту таблицу, легко вытянуть самое короткое расстояние от A до любого другого узла в графике:

Фракционная проблема рюкзака с использованием жадного алгоритма

Представь, что ты вор. Вы ломаетесь в дом Джуди Холлидей – 1951 победитель Оскара для лучшей актрисы Отказ Джуди – накопитель драгоценных камней. Дом Джуди подкладывается до краев с драгоценными камнями.

Вы принесли с собой сумку – рюкзак, если вы будете. Эта сумка имеет вес 7. Вы случились, чтобы иметь список всех предметов Джуди, от какого-то страхового документа. Предметы читаются как:

! []( https://skerritt.blog/content/images/2019/06/Screenshot_2019-06-23-Greedy-Algorithms-1-.png

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

И теперь мы жадно выбираем самые большие. Для этого мы можем отсортировать их в соответствии со значением/весом} в порядке убывания. К счастью для нас, они уже отсортированы. Самый большой 3,2.

knapsack value = 16
knapsack total weight = 5 (out of 7)

Затем мы выбираем Франция (я знаю, что это не драгоценный камень, но Джуди немного странно 😉)

knapsack value = 19
knapsack weight = 6

Теперь мы добавляем сапфир. Но если мы добавим Сапфиру, наш общий вес придет к 8.

В проблеме дробной рюкзаки мы можем сократить предметы, чтобы принять их доли. У нас есть вес 1 слева в сумке. Наш сапфир весом 2. Мы рассчитаем соотношение:

Вес рюкзака слева/вес предмета

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

1/2 *

knapsack value = 21
knapsack weight = 7

Жадный алгоритм можно оптимально решить проблему дробной рюкзак, но она не может оптимально решить проблему {0, 1} knaxack. В этой проблеме вместо того, чтобы принимать долю элемента, вы либо принимаете его {1}, либо вы не имеете {0}. Чтобы решить это, вам нужно использовать Динамическое программирование Отказ

время выполнения Для этого алгоритма o (n log n). Расчетное значение/вес – O (1). Наш главный шаг сортируется от наибольшего значения/веса, который принимает o (n log n) время.

Жадные VS Divide & Conquer VS Динамическое программирование

Вывод

Жадные алгоритмы очень быстро, но могут не обеспечивать оптимальное решение. Им также легче кодировать, чем их коллеги.

Оригинал: “https://dev.to/brandonskerritt/greedy-algorithms-for-beginners-4pho”