Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

NP-полнота Теория графов

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

Задача с маршрутом командировки

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

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

Нам нужно найти гамильтонов цикл с минимальным весом. Например, ниже показан граф с выделенным оптимальным решением:

eyJpZCI6IjllMjViNjliMjI2NDgzY2IwMmMyNzU0MTE1NzUyMTU5LnBuZyIsInN0b3JhZ2UiOiJjYWNoZSJ9?signature=a96204f526b4dfaa86ddd9539518bf31cc66803030943b75f22eef9319a4fd4b

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

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

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

Задача с временем выполнения

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

Предположим, у нас есть следующий код на языке Python, который суммирует все значения в списке:

total = 0
for i in range(len(L)):
total += L[i]

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

Получается, время работы этого алгоритма на конкретном компьютере может составлять микросекунды. Константы и будут варьироваться от компьютера к компьютеру. Нас интересует . Обычно это записывается как , что читается как «большое из ». Это говорит нам о порядке роста времени выполнения.

Так как порядок роста равен — это линейный алгоритм. Получается, если мы удвоим количество элементов в списке, время работы увеличится примерно вдвое. Если мы утроим количество элементов, время работы увеличится примерно втрое.

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

  • Если мы удвоим количество записей, то время работы увеличится примерно в четыре раза

  • Если мы утроим количество записей, то время работы увеличится примерно в девять раз

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

Любой алгоритм, время работы которого является полиномом, называется алгоритмом полиномиального времени. Для обозначения совокупности всех алгоритмов, выполняющихся за полиномиальное время, используется заглавная буква .

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

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

Предположим, что нам нужно выбрать один из двух алгоритмов, чтобы поработать с двумя графами:

  • Первый граф

  • Второй граф

На графе с вершинами у алгоритма будет время работы примерно единиц времени. Если эти единицы времени — микросекунды, то на выполнение алгоритма уйдет около секунды. С другой стороны, алгоритм занимает единицу времени, что намного больше, чем возраст Вселенной — это число с нулем. С практической точки зрения, экспоненциальный алгоритм непригоден для использования.

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

NP-полные проблемы

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

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

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

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

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

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

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

Проблема P=NP

В математике существует одна из самых важных проблем — проблемой . То есть никто не знает, равны ли между собой и . Если мы можем проверить правильность решения, значит ли это, что мы можем решить такую проблему? Не совсем. За решение этой проблемы полагается приз в миллион долларов. Большинство математиков и компьютерных ученых считают, что не равна , но не все с этим согласны. Правда пока никто не приблизился к решению проблемы.

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

Если проблема эйлеровой цепи находится в , значит, существует полиномиальный алгоритм, который определяет, имеет ли граф эйлерову цепь или нет. Перебор и проверка степеней каждой вершины, чтобы узнать, все ли они четные, — это полиномиальный алгоритм. Алгоритм Гирхольцера и алгоритм Флери для нахождения таких схем также являются полиномиальными алгоритмами.

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

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

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

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

Еще пример: поиск максимальных независимых множеств является -полной проблемой, но если мы просто хотим найти максимальные независимые множества на деревьях, то это является управляемой полиномиально-временной проблемой.

Выводы

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


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»
Изображение Тото

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