Жадные» алгоритмы. Алгоритм Краскала
Рассмотрим небольшую "детскую" задачу. Допустим, что у нас есть монеты достоинством 25, 10, 5 копеек и 1 копейка и нужно вернуть сдачу 63 копейки. Почти не раздумывая, мы преобразуем эту величину в две монеты по 25 копеек, одну монету в 10 копеек и три монеты по одной копейке. Нам не только удалось быстро определить перечень монет нужного достоинства, но и, по сути, мы составили самый короткий список монет требуемого достоинства.
На каждом шаге алгоритма мы выбирали монету наибольшего достоинства, которое удовлетворяло ограничениям нашей задачи. Эту монету мы исключали из общей суммы и вновь решали модифицированную задачу. Этот метод внесения изменений называется "жадным" алгоритмом. На каждой отдельной стадии "жадный" алгоритм выбирает тот вариант, который является локально оптимальным в том или ином смысле. Обратите внимание, что алгоритм для определения сдачи обеспечивает в целом оптимальное решение лишь вследствие особых свойств монет. Если бы у нас были монеты достоинством 1 копейка, 5 и 11 копеек и нужно было бы дать сдачу 15 копеек, то "жадный" алгоритм выбрал бы сначала монету достоинством 11 копеек, а затем четыре монеты по одной копейке, т.е. всего пять монет. Однако в данном случае можно было бы обойтись тремя монетами по 5 копеек.
Мы уже встречались в этой книге с несколькими "жадными" алгоритмами, например алгоритмом построения кратчайшего пути Дейкстры. Алгоритм кратчайшего пути Дейкстры является "жадным" в том смысле, что он всегда выбирает вершину, ближайшую к источнику, среди тех, кратчайший путь которых еще неизвестен.
Следует подчеркнуть, что не каждый "жадный" алгоритм позволяет получить оптимальный результат в целом. Как нередко бывает в жизни, "жадная стратегия" подчас обеспечивает лишь сиюминутную выгоду, в то время как в целом результат может оказаться неблагоприятным. Посмотрим, например, что произойдет, если в алгоритмах Дейкстры допустить наличие ребер с отрицательными весами. то алгоритм Дейкстры в некоторых случаях уже не позволяет получить кратчайшие пути.
Другим примером применения стратегии жадного алгоритма является алгоритм Крускала
«построения остовного дерева минимальной сложности в связном графе». Рассмотрим граф , ребрам которого приписаны некоторые веса. Требуется построить остовное дерево минимальной сложности (Под сложностью понимается сумма весов ребер, составляющих остовное дерево). Алгоритм Крускала является типичным представителем алгоритмов, в которых используются АТД «объединить, найти» В начальный момент времени дерево представляет собой множество одноэлементных подмножеств, каждое из которых содержит вершину графа. В процессе работы алгоритма ребро минимального веса попадает в остовное дерево, если оно на этот момент связывае различные множества вершин. После этого соответствующие множества сливаются различные подмножества сливаются, выбирается очередное ребро минимальной стоимости и процесс повторяется пока не будет получено единственное множество.
Алгоритм имеет сложность , где - число ребер графа.
"Жадный" алгоритм для задачи коммивояжера, речь о котором пойдет ниже, является вариантом алгоритма Крускала. Здесь, как и в основном алгоритме Крускала, сначала рассматриваются самые короткие ребра. В алгоритме Крускала очередное ребро принимается в том случае, если оно не образует цикл с уже принятыми ребрами; в противном случае ребро отвергается. В случае задачи коммивояжера "критерием принятия" ребра является то, что рассматриваемое ребро (в сочетании с уже принятыми ребрами)
• не приводит к появлению вершины со степенью три и более';
• не образует цикл (за исключением случая, когда количество выбранных ребер
равняется количеству вершин в рассматриваемой задаче).
Совокупности ребер, выбранных в соответствии с этими критериями, образуют совокупность несоединенных путей; такое положение сохраняется до выполнения последнего шага, когда единственный оставшийся путь замыкается, образуя маршрут.