HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>В прошлом уроке мы познакомились с задачей коммивояжера и решили ее с помощью перебора.</p>
1 <p>В прошлом уроке мы познакомились с задачей коммивояжера и решили ее с помощью перебора.</p>
2 <p>Перебор имеет алгоритмическую сложность O(n!), что очень медленно. Более эффективный способ решать задачи на графах - метод ветвей и границ.</p>
2 <p>Перебор имеет алгоритмическую сложность O(n!), что очень медленно. Более эффективный способ решать задачи на графах - метод ветвей и границ.</p>
3 <p>Он применяется к широкому классу задач на графах и для решения конкретной задачи его нужно адаптировать.</p>
3 <p>Он применяется к широкому классу задач на графах и для решения конкретной задачи его нужно адаптировать.</p>
4 <p>Алгоритм Литтла - известная адаптаций метода ветвей и границ для решения задачи коммивояжера. Его разработала группа ученых и программистов под руководством профессора Джона Д. К. Литтла. Статья с описанием алгоритма была опубликована в 1963 году.</p>
4 <p>Алгоритм Литтла - известная адаптаций метода ветвей и границ для решения задачи коммивояжера. Его разработала группа ученых и программистов под руководством профессора Джона Д. К. Литтла. Статья с описанием алгоритма была опубликована в 1963 году.</p>
5 <h2>Как работает алгоритм Литтла</h2>
5 <h2>Как работает алгоритм Литтла</h2>
6 <p>Алгоритм Литтла довольно громоздкий, так что мы будем знакомиться с ним по частям. Для представления графа в алгоритме используется матрица смежности. В качестве иллюстрации мы будем использовать матрицу из оригинальной публикации 1963 года:</p>
6 <p>Алгоритм Литтла довольно громоздкий, так что мы будем знакомиться с ним по частям. Для представления графа в алгоритме используется матрица смежности. В качестве иллюстрации мы будем использовать матрицу из оригинальной публикации 1963 года:</p>
7 <p>У нас есть шесть городов, поэтому в матрице шесть строк и шесть столбцов. Обсудим эту матрицу подробнее:</p>
7 <p>У нас есть шесть городов, поэтому в матрице шесть строк и шесть столбцов. Обсудим эту матрицу подробнее:</p>
8 <ul><li>Числа в матрице - это стоимость переезда из одного города в другой. Это условная цифра, которая может обозначать цену бензина, расстояние между городами или время езды.</li>
8 <ul><li>Числа в матрице - это стоимость переезда из одного города в другой. Это условная цифра, которая может обозначать цену бензина, расстояние между городами или время езды.</li>
9 <li>Обратите внимание, что граф ориентированный. Это значит, что стоимость перемещения из города A в город B и из города B в город A необязательно равны между собой. В неориентированном графе эти стоимости совпадают.</li>
9 <li>Обратите внимание, что граф ориентированный. Это значит, что стоимость перемещения из города A в город B и из города B в город A необязательно равны между собой. В неориентированном графе эти стоимости совпадают.</li>
10 </ul><p>Для примера рассмотрим дорогу между городами 1 и 4. Находим соответствующие столбцы и строки в матрице и видим два разных числа:</p>
10 </ul><p>Для примера рассмотрим дорогу между городами 1 и 4. Находим соответствующие столбцы и строки в матрице и видим два разных числа:</p>
11 <ul><li>16 в первой строке в четвертом столбце - это стоимость перемещения из города 1 в город 4.</li>
11 <ul><li>16 в первой строке в четвертом столбце - это стоимость перемещения из города 1 в город 4.</li>
12 <li>21 в четвертой строке в первом столбце - это стоимость перемещения из города 4 в город 1.</li>
12 <li>21 в четвертой строке в первом столбце - это стоимость перемещения из города 4 в город 1.</li>
13 </ul><p>Некоторые ребра в графе не могут существовать физически - например, нельзя переместиться в тот же самый город. В матрице смежности таким переездам соответствуют ячейки:</p>
13 </ul><p>Некоторые ребра в графе не могут существовать физически - например, нельзя переместиться в тот же самый город. В матрице смежности таким переездам соответствуют ячейки:</p>
14 <ul><li>В первой строке первом столбце</li>
14 <ul><li>В первой строке первом столбце</li>
15 <li>Во второй строке втором столбце</li>
15 <li>Во второй строке втором столбце</li>
16 <li>И так далее</li>
16 <li>И так далее</li>
17 </ul><p>Чтобы отличить невозможные переезды от возможных, мы придумаем для них особое обозначение.</p>
17 </ul><p>Чтобы отличить невозможные переезды от возможных, мы придумаем для них особое обозначение.</p>
18 <p>В коде на JavaScript можно использовать константу Infinity - она соответствует бесконечности, которая больше любого конечного значения.</p>
18 <p>В коде на JavaScript можно использовать константу Infinity - она соответствует бесконечности, которая больше любого конечного значения.</p>
19 <p>На иллюстрациях мы будем писать знак ∞ - символ бесконечности.</p>
19 <p>На иллюстрациях мы будем писать знак ∞ - символ бесконечности.</p>
20 <p>В матрице смежности маршрут записывается как последовательность переездов r:</p>
20 <p>В матрице смежности маршрут записывается как последовательность переездов r:</p>
21 <p>Длина этого маршрута l равна:</p>
21 <p>Длина этого маршрута l равна:</p>
22 <p>Каждый город встречается в этом списке ровно два раза:</p>
22 <p>Каждый город встречается в этом списке ровно два раза:</p>
23 <ul><li>В строке - точка отправления</li>
23 <ul><li>В строке - точка отправления</li>
24 <li>В столбце - точка прибытия</li>
24 <li>В столбце - точка прибытия</li>
25 </ul><p>Это не совпадение - по условиям задачи, коммивояжер должен посетить каждый город ровно один раз. Мы можем отметить ребра маршрута на матрице смежности и увидеть, что в каждой строке и каждом столбце находится ровно одно ребро:</p>
25 </ul><p>Это не совпадение - по условиям задачи, коммивояжер должен посетить каждый город ровно один раз. Мы можем отметить ребра маршрута на матрице смежности и увидеть, что в каждой строке и каждом столбце находится ровно одно ребро:</p>
26 <p>Это утверждение верно для всех маршрутов, удовлетворяющих условию задачи коммивояжера. Из этого следует два интересных факта:</p>
26 <p>Это утверждение верно для всех маршрутов, удовлетворяющих условию задачи коммивояжера. Из этого следует два интересных факта:</p>
27 <ul><li>Если из всех элементов строки или столбца вычесть одно и то же число, то все маршруты сократятся на это число</li>
27 <ul><li>Если из всех элементов строки или столбца вычесть одно и то же число, то все маршруты сократятся на это число</li>
28 <li>После такой модификации самый короткий маршрут останется самым коротким, а самый длинный - самым длинным</li>
28 <li>После такой модификации самый короткий маршрут останется самым коротким, а самый длинный - самым длинным</li>
29 </ul><p>Алгоритм Литтла использует эти свойства для вычисления нижней границы.</p>
29 </ul><p>Алгоритм Литтла использует эти свойства для вычисления нижней границы.</p>
30 <h3>Нижняя граница</h3>
30 <h3>Нижняя граница</h3>
31 <p>Обозначим длину кратчайшего маршрута как l_min. Эта длина не может быть меньше нуля, потому что в каждой ячейке матрицы находится положительные числа или ноль.</p>
31 <p>Обозначим длину кратчайшего маршрута как l_min. Эта длина не может быть меньше нуля, потому что в каждой ячейке матрицы находится положительные числа или ноль.</p>
32 <p>Возьмем число a. Если мы вычтем его из всех чисел строки или всех чисел столбца, то все маршруты сократятся на a.</p>
32 <p>Возьмем число a. Если мы вычтем его из всех чисел строки или всех чисел столбца, то все маршруты сократятся на a.</p>
33 <p>Если при этом все элементы в матрице останутся неотрицательными, то и новая длина маршрута будет больше нуля:</p>
33 <p>Если при этом все элементы в матрице останутся неотрицательными, то и новая длина маршрута будет больше нуля:</p>
34 <ul><li>l_min - a &gt;= 0</li>
34 <ul><li>l_min - a &gt;= 0</li>
35 <li>или l_min &gt;= a</li>
35 <li>или l_min &gt;= a</li>
36 </ul><p>Возьмем нашу матрицу и найдем минимальное число в каждой строке. Запишем его справа от строки:</p>
36 </ul><p>Возьмем нашу матрицу и найдем минимальное число в каждой строке. Запишем его справа от строки:</p>
37 <p>Вычтем из каждой строки минимальное число. Обратите внимание, что после такого вычитания все ячейки в матрице останутся неотрицательными:</p>
37 <p>Вычтем из каждой строки минимальное число. Обратите внимание, что после такого вычитания все ячейки в матрице останутся неотрицательными:</p>
38 <p>При этом минимальная длина будет больше суммы чисел, которые мы вычли:</p>
38 <p>При этом минимальная длина будет больше суммы чисел, которые мы вычли:</p>
39 <p>l_min &gt;= 16+1+0+16+5</p>
39 <p>l_min &gt;= 16+1+0+16+5</p>
40 <p>Такая операция в алгоритме Литтла называется<strong>редукцией по строкам</strong>.</p>
40 <p>Такая операция в алгоритме Литтла называется<strong>редукцией по строкам</strong>.</p>
41 <p>Как мы говорили выше, подобную редукцию можно сделать и по столбцам. Найдем минимальное число в каждом столбце. Запишем его под столбцом:</p>
41 <p>Как мы говорили выше, подобную редукцию можно сделать и по столбцам. Найдем минимальное число в каждом столбце. Запишем его под столбцом:</p>
42 <p>Выполним<strong>редукцию столбцов</strong>- вычтем минимальное число из каждого столбца:</p>
42 <p>Выполним<strong>редукцию столбцов</strong>- вычтем минимальное число из каждого столбца:</p>
43 <p>Все числа в матрице все еще неотрицательные, поэтому можно утверждать следующее:</p>
43 <p>Все числа в матрице все еще неотрицательные, поэтому можно утверждать следующее:</p>
44 <ul><li>l_min &gt;= (16+1+0+16+5)+(5+0+0+0+0+0)</li>
44 <ul><li>l_min &gt;= (16+1+0+16+5)+(5+0+0+0+0+0)</li>
45 <li>l_min &gt;= 43+5</li>
45 <li>l_min &gt;= 43+5</li>
46 <li>l_min &gt;= 48</li>
46 <li>l_min &gt;= 48</li>
47 </ul><p>Таким образом, кратчайший маршрут в графе не может быть меньше 48. Cледовательно, 48 - это и есть<strong>нижняя оценка длины маршрута</strong>.</p>
47 </ul><p>Таким образом, кратчайший маршрут в графе не может быть меньше 48. Cледовательно, 48 - это и есть<strong>нижняя оценка длины маршрута</strong>.</p>
48 <h3>Ветвление</h3>
48 <h3>Ветвление</h3>
49 <p>На следующем шаге построим два поддерева. В начале работы алгоритма наш единственный узел - корень дерева, которое содержит все возможные маршруты:</p>
49 <p>На следующем шаге построим два поддерева. В начале работы алгоритма наш единственный узел - корень дерева, которое содержит все возможные маршруты:</p>
50 <p>Сейчас мы знаем два факта:</p>
50 <p>Сейчас мы знаем два факта:</p>
51 <ul><li>Нижняя граница длины маршрута в этом дереве равна 48</li>
51 <ul><li>Нижняя граница длины маршрута в этом дереве равна 48</li>
52 <li>Пока у нас пока нет никакого маршрута, даже частичного</li>
52 <li>Пока у нас пока нет никакого маршрута, даже частичного</li>
53 </ul><p>Предположим, в качестве очередного ребра маршрута мы выбрали 1-4. Построим два дочерних поддерева:</p>
53 </ul><p>Предположим, в качестве очередного ребра маршрута мы выбрали 1-4. Построим два дочерних поддерева:</p>
54 <p>На половинах дерева мы видим два варианта:</p>
54 <p>На половинах дерева мы видим два варианта:</p>
55 <ul><li>Поддерево справа содержит маршруты, где есть ребро 1-4</li>
55 <ul><li>Поддерево справа содержит маршруты, где есть ребро 1-4</li>
56 <li>Поддерево слева - маршруты, где его нет</li>
56 <li>Поддерево слева - маршруты, где его нет</li>
57 </ul><p>Ребро 1-4 в левом узле помечено красным цветом, который будет означать, что ребро отсутствует. В фигурных скобках записываем маршрут, построенный к настоящему моменту.</p>
57 </ul><p>Ребро 1-4 в левом узле помечено красным цветом, который будет означать, что ребро отсутствует. В фигурных скобках записываем маршрут, построенный к настоящему моменту.</p>
58 <h3>Левое поддерево</h3>
58 <h3>Левое поддерево</h3>
59 <p>Посмотрим, как изменится нижняя оценка для левого поддерева. Взглянем на редуцированную матрицу и выделим цветом ячейку, которая соответствует ребру 1-4:</p>
59 <p>Посмотрим, как изменится нижняя оценка для левого поддерева. Взглянем на редуцированную матрицу и выделим цветом ячейку, которая соответствует ребру 1-4:</p>
60 <p>Рассмотрим каждую точку по отдельности. Сначала обсудим город 1:</p>
60 <p>Рассмотрим каждую точку по отдельности. Сначала обсудим город 1:</p>
61 <ul><li>Из него можно приехать в города 2,3,4,5 и 6</li>
61 <ul><li>Из него можно приехать в города 2,3,4,5 и 6</li>
62 <li>Исключаем ребро 1-4 из маршрута и оставим города 2, 3, 5 и 6</li>
62 <li>Исключаем ребро 1-4 из маршрута и оставим города 2, 3, 5 и 6</li>
63 <li>Стоимость переезда в эти города равна 11, 27, 14 и 10</li>
63 <li>Стоимость переезда в эти города равна 11, 27, 14 и 10</li>
64 <li>Куда бы мы не переехали, стоимость не может быть меньше 10 - это число мы видим в синей ячейке в первой строке</li>
64 <li>Куда бы мы не переехали, стоимость не может быть меньше 10 - это число мы видим в синей ячейке в первой строке</li>
65 </ul><p>Перейдем к городу 4:</p>
65 </ul><p>Перейдем к городу 4:</p>
66 <ul><li>Из него можно приехать в города 1, 2, 3, 5 и 6</li>
66 <ul><li>Из него можно приехать в города 1, 2, 3, 5 и 6</li>
67 <li>Исключаем ребро 1-4 и оставляем города 2, 3, 5 и 6</li>
67 <li>Исключаем ребро 1-4 и оставляем города 2, 3, 5 и 6</li>
68 <li>Стоимость переезда из этих городов равна 0, 35, 43 и 4</li>
68 <li>Стоимость переезда из этих городов равна 0, 35, 43 и 4</li>
69 <li>Откуда бы мы не переехали, стоимость не может быть меньше 0 - это число мы видим в синей ячейке в четвертом столбце</li>
69 <li>Откуда бы мы не переехали, стоимость не может быть меньше 0 - это число мы видим в синей ячейке в четвертом столбце</li>
70 </ul><p>В итоге мы приходим к выводу - мы увеличим редуцированный маршрут минимум на 10+0 в двух случаях:</p>
70 </ul><p>В итоге мы приходим к выводу - мы увеличим редуцированный маршрут минимум на 10+0 в двух случаях:</p>
71 <ul><li>Уехав из города 1 в любой город, кроме 4</li>
71 <ul><li>Уехав из города 1 в любой город, кроме 4</li>
72 <li>Приехав в город 4 из любого города, кроме 1</li>
72 <li>Приехав в город 4 из любого города, кроме 1</li>
73 </ul><p>В то же время, если бы мы воспользовались ребром 1-4, редуцированный маршрут остался бы прежним. Так произошло бы, потому что в ячейке на пересечении первой строки и четвертого столбца сейчас находится 0.</p>
73 </ul><p>В то же время, если бы мы воспользовались ребром 1-4, редуцированный маршрут остался бы прежним. Так произошло бы, потому что в ячейке на пересечении первой строки и четвертого столбца сейчас находится 0.</p>
74 <p>Можно сказать, что 10 - это штраф за отказ от ребра 1-4. Нам известна нижняя граница кратчайшего пути - 48. Без ребра 1-4 она увеличится на 10 и станет равна 48+10=58:</p>
74 <p>Можно сказать, что 10 - это штраф за отказ от ребра 1-4. Нам известна нижняя граница кратчайшего пути - 48. Без ребра 1-4 она увеличится на 10 и станет равна 48+10=58:</p>
75 <p>Предположим, на одном из этапов мы достроим одну из ветвей дерева до конца и получим вариант маршрута с длиной 56.</p>
75 <p>Предположим, на одном из этапов мы достроим одну из ветвей дерева до конца и получим вариант маршрута с длиной 56.</p>
76 <p>Число 58 больше 56, поэтому мы можем игнорировать левое поддерево - маршрут с длиной 56 заведомо короче всех его маршрутов.</p>
76 <p>Число 58 больше 56, поэтому мы можем игнорировать левое поддерево - маршрут с длиной 56 заведомо короче всех его маршрутов.</p>
77 <p>Чем выше нижняя граница в поддереве, тем больше шансов, что поддерево удастся отсечь. Именно поэтому полезно выбирать ребро с небольшим штрафом.</p>
77 <p>Чем выше нижняя граница в поддереве, тем больше шансов, что поддерево удастся отсечь. Именно поэтому полезно выбирать ребро с небольшим штрафом.</p>
78 <p><strong>Штраф для элемента матрицы</strong>- это сумма минимумов в той же строке и том же столбце. Сам элемент учитывать не надо.</p>
78 <p><strong>Штраф для элемента матрицы</strong>- это сумма минимумов в той же строке и том же столбце. Сам элемент учитывать не надо.</p>
79 <p>Если мы будем выбирать элементы 0, минимумами будут нули для которых суммарный штраф также будет равен 0. Чтобы найти ребро с наибольшим штрафом, достаточно проверять только нулевые элементы матрицы:</p>
79 <p>Если мы будем выбирать элементы 0, минимумами будут нули для которых суммарный штраф также будет равен 0. Чтобы найти ребро с наибольшим штрафом, достаточно проверять только нулевые элементы матрицы:</p>
80 <p>На рисунке цветом выделены все нулевые элементы. Рядом с каждым записан суммарный штраф. Максимальный штраф 10 - он как раз и соответствует ребру 1-4:</p>
80 <p>На рисунке цветом выделены все нулевые элементы. Рядом с каждым записан суммарный штраф. Максимальный штраф 10 - он как раз и соответствует ребру 1-4:</p>
81 <h3>Исключение ребра</h3>
81 <h3>Исключение ребра</h3>
82 <p>Спускаясь по левому поддереву, мы должны помнить, что ребро 1-4 в маршрут включать нельзя. Алгоритм Литтла предлагает хранить ее непосредственно в матрице смежности, поместив особое значение в первую строку четвертого столбца. Как и раньше, мы можем хранить очень большое число или константу Infinity.</p>
82 <p>Спускаясь по левому поддереву, мы должны помнить, что ребро 1-4 в маршрут включать нельзя. Алгоритм Литтла предлагает хранить ее непосредственно в матрице смежности, поместив особое значение в первую строку четвертого столбца. Как и раньше, мы можем хранить очень большое число или константу Infinity.</p>
83 <p>Рассмотрим новую матрицу для левого поддерева. Обратите внимание, что она может оказаться нередуцированной, как получилось в нашем случае. Редуцируем ее по первой строке четвертого столбца:</p>
83 <p>Рассмотрим новую матрицу для левого поддерева. Обратите внимание, что она может оказаться нередуцированной, как получилось в нашем случае. Редуцируем ее по первой строке четвертого столбца:</p>
84 <h3>Правое поддерево</h3>
84 <h3>Правое поддерево</h3>
85 <p>Если мы переместились по маршруту 1→4, мы больше не можем вернуться в город 1 и уехать в любой другой город. Так происходит, потому что коммивояжер может посетить каждый город только один раз.</p>
85 <p>Если мы переместились по маршруту 1→4, мы больше не можем вернуться в город 1 и уехать в любой другой город. Так происходит, потому что коммивояжер может посетить каждый город только один раз.</p>
86 <p>Это значит, что выбрав ребро 1-4, мы одновременно должны вычеркнуть первую строку четвертого столбца. Другими словами, нужно исключить из матрицы следующие ребра:</p>
86 <p>Это значит, что выбрав ребро 1-4, мы одновременно должны вычеркнуть первую строку четвертого столбца. Другими словами, нужно исключить из матрицы следующие ребра:</p>
87 <ul><li>1→2</li>
87 <ul><li>1→2</li>
88 <li>1→3</li>
88 <li>1→3</li>
89 <li>1→5</li>
89 <li>1→5</li>
90 <li>1→6</li>
90 <li>1→6</li>
91 <li>2→4</li>
91 <li>2→4</li>
92 <li>3→4</li>
92 <li>3→4</li>
93 <li>5→4</li>
93 <li>5→4</li>
94 <li>6→4</li>
94 <li>6→4</li>
95 </ul><p>Простой способ вычеркнуть строку и столбец - заполнить их значением ∞.</p>
95 </ul><p>Простой способ вычеркнуть строку и столбец - заполнить их значением ∞.</p>
96 <p>Кроме того, переехав по маршруту 1→4 мы не можем вернуться. Поэтому ребро 4-1 также можно исключить, записав в соответствующую ячейку значение ∞.</p>
96 <p>Кроме того, переехав по маршруту 1→4 мы не можем вернуться. Поэтому ребро 4-1 также можно исключить, записав в соответствующую ячейку значение ∞.</p>
97 <p>На рисунке показана матрица после этих преобразований:</p>
97 <p>На рисунке показана матрица после этих преобразований:</p>
98 <p>Ее можно редуцировать, вычтя минимумы из каждой строки и каждого столбца:</p>
98 <p>Ее можно редуцировать, вычтя минимумы из каждой строки и каждого столбца:</p>
99 <p>Посчитаем сумму минимумов:</p>
99 <p>Посчитаем сумму минимумов:</p>
100 <p>Эту сумму минимумов надо прибавить к предыдущей нижней границе. Так мы получим нижнюю границу правого поддерева 48+1=49:</p>
100 <p>Эту сумму минимумов надо прибавить к предыдущей нижней границе. Так мы получим нижнюю границу правого поддерева 48+1=49:</p>
101 <h3>Цикл</h3>
101 <h3>Цикл</h3>
102 <p>Таким образом, мы построили два поддерева. Пока у нас не хватает информации для отсечения, поэтому мы оставляем оба поддерева и выбираем одно из них для последующего ветвления. Разумно выбирать узел с наименьшей нижней границей. В нашем примере это правый узел, включающий ребро 1-4.</p>
102 <p>Таким образом, мы построили два поддерева. Пока у нас не хватает информации для отсечения, поэтому мы оставляем оба поддерева и выбираем одно из них для последующего ветвления. Разумно выбирать узел с наименьшей нижней границей. В нашем примере это правый узел, включающий ребро 1-4.</p>
103 <p>Берем матрицу правого поддерева. Она уже редуцирована, так что мы ищем нулевой элемент с максимальным штрафом:</p>
103 <p>Берем матрицу правого поддерева. Она уже редуцирована, так что мы ищем нулевой элемент с максимальным штрафом:</p>
104 <p>Максимальный штраф - 16, он соответствует ребру 2-1. Его мы и выберем для ветвления дерева. Как и на первом шаге, левое поддерево будет соответствовать маршрутам без ребра 2-1. В матрице левого поддерева достаточно записать ∞ во вторую строку первого столбца:</p>
104 <p>Максимальный штраф - 16, он соответствует ребру 2-1. Его мы и выберем для ветвления дерева. Как и на первом шаге, левое поддерево будет соответствовать маршрутам без ребра 2-1. В матрице левого поддерева достаточно записать ∞ во вторую строку первого столбца:</p>
105 <p>В правом поддереве мы вычеркиваем вторую строку первого столбца - заполняем их значением ∞.</p>
105 <p>В правом поддереве мы вычеркиваем вторую строку первого столбца - заполняем их значением ∞.</p>
106 <p>Также нам надо избавиться от обратного ребра 1-2. Но оно уже вычеркнуто из матрицы на предыдущем шаге, так что мы ничего дополнительно не делаем:</p>
106 <p>Также нам надо избавиться от обратного ребра 1-2. Но оно уже вычеркнуто из матрицы на предыдущем шаге, так что мы ничего дополнительно не делаем:</p>
107 <p>В алгоритме Литтла маршрут не строится последовательно, как это было в методе перебора. На каждом шаге мы выбираем ребро с максимальным штрафом. Два последовательно выбранных ребра могут даже не соединяться друг с другом.</p>
107 <p>В алгоритме Литтла маршрут не строится последовательно, как это было в методе перебора. На каждом шаге мы выбираем ребро с максимальным штрафом. Два последовательно выбранных ребра могут даже не соединяться друг с другом.</p>
108 <p>В нашем примере ребра 1-4 и 2-1 соединены через вершину 1. Добавим к ним ребро 4-2 и замкнем маршрут - правда, он будет пролегать не по всем шести городам, а только по трем.</p>
108 <p>В нашем примере ребра 1-4 и 2-1 соединены через вершину 1. Добавим к ним ребро 4-2 и замкнем маршрут - правда, он будет пролегать не по всем шести городам, а только по трем.</p>
109 <p>Это нарушает условие задачи коммивояжера, поэтому мы должны исключить ребро 4-2 из матрицы, поместив в ячейку ∞.</p>
109 <p>Это нарушает условие задачи коммивояжера, поэтому мы должны исключить ребро 4-2 из матрицы, поместив в ячейку ∞.</p>
110 <p>Делаем редукцию получившейся матрицы:</p>
110 <p>Делаем редукцию получившейся матрицы:</p>
111 <p>Нижняя граница для правого поддерева увеличивается на:</p>
111 <p>Нижняя граница для правого поддерева увеличивается на:</p>
112 <p>(2+0+0+0)+(0+0+0+0 )=2</p>
112 <p>(2+0+0+0)+(0+0+0+0 )=2</p>
113 <p>В итоге новая нижняя граница будет равна 49+2=51:</p>
113 <p>В итоге новая нижняя граница будет равна 49+2=51:</p>
114 <p>Сейчас у нас есть список вершин, доступных для ветвления. В нем находятся вершины с нижней границей 58, 65 и 51. Как и раньше, продолжим ветвление узла с наименьшей оценкой.</p>
114 <p>Сейчас у нас есть список вершин, доступных для ветвления. В нем находятся вершины с нижней границей 58, 65 и 51. Как и раньше, продолжим ветвление узла с наименьшей оценкой.</p>
115 <p>Выбираем ребро 5-6 с наибольшим штрафом (22):</p>
115 <p>Выбираем ребро 5-6 с наибольшим штрафом (22):</p>
116 <p>Продолжаем ветвление узла с наименьшей оценкой 56. Здесь максимальный штраф 8 соответствует ребрам 3-5 и 6-2. Для ветвления мы можем выбрать любое ребро, для примера пусть будет 3-5:</p>
116 <p>Продолжаем ветвление узла с наименьшей оценкой 56. Здесь максимальный штраф 8 соответствует ребрам 3-5 и 6-2. Для ветвления мы можем выбрать любое ребро, для примера пусть будет 3-5:</p>
117 <p>В самом правом поддереве можно образовать короткий маршрут из трех ребер:</p>
117 <p>В самом правом поддереве можно образовать короткий маршрут из трех ребер:</p>
118 <ul><li>3-5</li>
118 <ul><li>3-5</li>
119 <li>5-6</li>
119 <li>5-6</li>
120 <li>6-3</li>
120 <li>6-3</li>
121 </ul><p>Этот короткий цикл не является решением, поэтому вычеркиваем ребро 6-3 из матрицы.</p>
121 </ul><p>Этот короткий цикл не является решением, поэтому вычеркиваем ребро 6-3 из матрицы.</p>
122 <p>На этом этапе мы уже привыкли к тому, что продолжаем ветвление самого правого поддерева. Но сейчас нижняя граница длины маршрута в этом поддереве равна 63, хотя вверху слева у нас есть поддерево с оценкой 58.</p>
122 <p>На этом этапе мы уже привыкли к тому, что продолжаем ветвление самого правого поддерева. Но сейчас нижняя граница длины маршрута в этом поддереве равна 63, хотя вверху слева у нас есть поддерево с оценкой 58.</p>
123 <p>Возвращаемся к узлу, где вычеркнуто ребро 1-4. Разбиваем его на два поддерева:</p>
123 <p>Возвращаемся к узлу, где вычеркнуто ребро 1-4. Разбиваем его на два поддерева:</p>
124 <p>Среди всех ребер есть 6-3 с максимальным штрафом - 9. Таким образом:</p>
124 <p>Среди всех ребер есть 6-3 с максимальным штрафом - 9. Таким образом:</p>
125 <ul><li>Стоимость левого поддерева равна 58+9=67, если вычеркнуть ребро 6-3</li>
125 <ul><li>Стоимость левого поддерева равна 58+9=67, если вычеркнуть ребро 6-3</li>
126 <li>Стоимость правого поддерева равна 58+5=63, если вычеркнуть ячейку 3-6 и шестую строку третьего столбца</li>
126 <li>Стоимость правого поддерева равна 58+5=63, если вычеркнуть ячейку 3-6 и шестую строку третьего столбца</li>
127 </ul><p>На схеме это выглядит так:</p>
127 </ul><p>На схеме это выглядит так:</p>
128 <p>На этом этапе мы ни разу не проводили отсечение, потому что пока у нас нет ни одного построенного маршрута.</p>
128 <p>На этом этапе мы ни разу не проводили отсечение, потому что пока у нас нет ни одного построенного маршрута.</p>
129 <p>Сейчас в дереве решений есть два узла с минимальной нижней границей 63. Мы можем выбрать любое из них. Выберем самое правое поддерево и посмотрим на его матрицу:</p>
129 <p>Сейчас в дереве решений есть два узла с минимальной нижней границей 63. Мы можем выбрать любое из них. Выберем самое правое поддерево и посмотрим на его матрицу:</p>
130 <p>В этот момент мы останавливаемся, потому что у нас останется только два решения - два значения в матрице, которые меньше ∞.</p>
130 <p>В этот момент мы останавливаемся, потому что у нас останется только два решения - два значения в матрице, которые меньше ∞.</p>
131 <p>Фактически, сейчас у нас нет выбора - нам нужно найти два недостающие ребра и вставить их в маршрут в произвольном порядке. В нашем случае это ребра 4-3 и 6-2. Если бы в ячейках были ненулевые значения, то нам бы потребовалась редукция - в данном случае она не нужна.</p>
131 <p>Фактически, сейчас у нас нет выбора - нам нужно найти два недостающие ребра и вставить их в маршрут в произвольном порядке. В нашем случае это ребра 4-3 и 6-2. Если бы в ячейках были ненулевые значения, то нам бы потребовалась редукция - в данном случае она не нужна.</p>
132 <p>Нижняя граница длины маршрута в поддереве равна единственному маршруту в нем - а именно 63:</p>
132 <p>Нижняя граница длины маршрута в поддереве равна единственному маршруту в нем - а именно 63:</p>
133 <p>Итак, мы построили первый маршрут длиной 63. У нас есть недостроенные поддеревья с нижними границами 67, 63, 73 и 64. Нет ни одного поддерева с нижней границей меньше 63. Это значит, что в дереве решений точно нет маршрута, который мы уже нашли. Работу алгоритма можно завершить.</p>
133 <p>Итак, мы построили первый маршрут длиной 63. У нас есть недостроенные поддеревья с нижними границами 67, 63, 73 и 64. Нет ни одного поддерева с нижней границей меньше 63. Это значит, что в дереве решений точно нет маршрута, который мы уже нашли. Работу алгоритма можно завершить.</p>
134 <p>Если бы у нас оказалось несколько узлов с меньшей нижней границей, мы бы оставили только их, а остальные бы отсекли.</p>
134 <p>Если бы у нас оказалось несколько узлов с меньшей нижней границей, мы бы оставили только их, а остальные бы отсекли.</p>
135 <p>Самый короткий маршрут из найденных называется<strong>рекордным маршрутом</strong>или<strong>рекордом</strong>. На каждом шаге алгоритма мы можем отсекать поддеревья с нижней границей, которая больше или равна длине рекордного маршрута.</p>
135 <p>Самый короткий маршрут из найденных называется<strong>рекордным маршрутом</strong>или<strong>рекордом</strong>. На каждом шаге алгоритма мы можем отсекать поддеревья с нижней границей, которая больше или равна длине рекордного маршрута.</p>
136 <p>Мы последовательно достраиваем очередное поддерево до конца и получаем новый маршрут. При этом мы должны постоянно проверять, не короче ли новый маршрут текущего рекорда. Если короче, он сам должен стать новым рекордным маршрутом.</p>
136 <p>Мы последовательно достраиваем очередное поддерево до конца и получаем новый маршрут. При этом мы должны постоянно проверять, не короче ли новый маршрут текущего рекорда. Если короче, он сам должен стать новым рекордным маршрутом.</p>
137 <h2>Выводы</h2>
137 <h2>Выводы</h2>
138 <p>В этом уроке мы познакомились с алгоритмом Литтла. Повторим ключевые выводы:</p>
138 <p>В этом уроке мы познакомились с алгоритмом Литтла. Повторим ключевые выводы:</p>
139 <ul><li>Алгоритм Литтла находит оптимальное решение в дереве решений</li>
139 <ul><li>Алгоритм Литтла находит оптимальное решение в дереве решений</li>
140 <li>Для работы алгоритма важно, чтобы оценка нижней границы была корректной</li>
140 <li>Для работы алгоритма важно, чтобы оценка нижней границы была корректной</li>
141 <li>Обычно алгоритм Литтла работает быстрее метода перебора, в худшем случае - с той же скоростью O(n!)</li>
141 <li>Обычно алгоритм Литтла работает быстрее метода перебора, в худшем случае - с той же скоростью O(n!)</li>
142 </ul><p>Пока наше знакомство ограничилось только теорией, но уже в следующем уроке мы разберем, как реализовать алгоритм Литтла на практике.</p>
142 </ul><p>Пока наше знакомство ограничилось только теорией, но уже в следующем уроке мы разберем, как реализовать алгоритм Литтла на практике.</p>