# теория · шаг 1 из 6
Граф: вершины, рёбра и словарь, без которого дальше никак
Коротко
Граф - это множество вершин и множество рёбер, каждое из которых соединяет пару
вершин. Всё. Из этой бедности и растёт его сила: дороги, сети, зависимости пакетов, ссылки между
страницами, переходы состояний, знакомства людей - всё это один и тот же объект, и алгоритм,
написанный для графа, работает для каждого из них. Различать придётся немногое:
ориентированный граф (ребро ведёт в одну сторону) против неориентированного,
взвешенный (у ребра есть число) против невзвешенного, простой (без петель и кратных
рёбер) против мультиграфа. Два числа определяют всё дальнейшее: V - число вершин, E - число
рёбер, и E лежит между 0 и примерно V².
Если ты уверенно отличаешь ориентированный граф от неориентированного, знаешь, что такое степень, путь, цикл, связность и дерево как частный случай графа, и помнишь границы E, листай до «Теперь сам».
Как думаешь, что общего у метро, зависимостей пакетов и шахмат
Прежде чем читать дальше, найди общее в трёх задачах. Первая: доехать от одной станции метро до другой с наименьшим числом пересадок. Вторая: установить пакет, у которого есть зависимости, а у тех свои - в каком порядке ставить. Третья: добраться конём с одной клетки шахматной доски на другую за наименьшее число ходов. Задачи из разных миров, но решает их один и тот же код: в каждой есть ОБЪЕКТЫ и ПЕРЕХОДЫ между ними, а вопрос - про достижимость или порядок.
На что это похоже
Карта с городами и дорогами. Города - вершины, дороги - рёбра. Дорога с односторонним движением - ориентированное ребро, обычная - неориентированное. Длина дороги в километрах - вес. Если из города есть кольцевая дорога в него же - это петля. Если два города соединены двумя разными трассами - кратные рёбра. Всё, что дальше будет в разделе, - это вопросы к такой карте: можно ли доехать, за сколько, какой мост разорвёт страну надвое, в каком порядке обходить.
Механизм: словарь, который нужен дальше
вершина (vertex, node) - объект; их число обозначают V
ребро (edge) - пара вершин; их число обозначают E
ориентированное ребро - упорядоченная пара (u → v): из u в v можно, обратно нет
неориентированное - неупорядоченная пара {u, v}: ходить можно в обе стороны
вес - число на ребре (длина, цена, пропускная способность)
петля - ребро из вершины в неё же
кратные рёбра - два и более рёбер между одной парой вершин
простой граф - без петель и кратных рёбер
мультиграф - где они разрешены
Вторая половина словаря - про то, что в графе можно найти:
смежные вершины - соединённые ребром
инцидентность - отношение «вершина - её ребро»
степень deg(v) - число рёбер при вершине (в ориентированном: входящая и исходящая
степень отдельно)
путь - последовательность вершин, где соседние соединены ребром
простой путь - путь без повторяющихся вершин
цикл - замкнутый путь, НЕ повторяющий рёбер (в простом цикле не
повторяются и вершины, кроме совпадающих концов). Без этого
условия «сходить к соседу и вернуться» считалось бы циклом,
и деревьев не существовало бы вовсе
связный граф - из любой вершины достижима любая другая (в неориентированном)
компонента связности - максимальный кусок, внутри которого это верно
дерево - связный граф без циклов; в нём ровно V − 1 рёбер
Заметь: дерево - частный случай графа, а деревья ты уже проходил. Значит обходы, которые ты знаешь по деревьям, - это частный случай обходов графа. Разница будет ровно одна, и о ней следующая глава.
Правило
Граф - пара «множество вершин, множество рёбер»; их количества обозначают V и E (дальше эти
буквы всюду означают ЧИСЛА). Ребро соединяет две вершины и бывает
ориентированным (в одну сторону) и взвешенным (с числом). Степень вершины - число её рёбер; путь -
цепочка вершин по рёбрам; цикл - путь, вернувшийся в начало; связный граф - тот, где из любой
вершины достижима любая. Дерево - связный граф без циклов, в нём ровно V − 1 рёбер. Число рёбер
лежит между 0 и V·(V−1)/2 для простого неориентированного графа (то есть O(V²)), и от того,
ближе оно к V или к V², зависят все дальнейшие решения.
Разбор: сколько рёбер бывает и что значит «плотный»
Посчитаем границы. В простом неориентированном графе каждая пара вершин соединена не больше одного
раза, пар всего C(V, 2) = V·(V−1)/2, значит E <= V·(V−1)/2 ≈ V²/2. В ориентированном пар
упорядоченных вдвое больше: E <= V·(V−1) ≈ V². Минимум - ноль рёбер (все вершины по отдельности),
а чтобы граф был связным, нужно минимум V − 1 ребро (это и есть дерево).
Отсюда два слова, которые дальше будут встречаться постоянно:
разреженный (sparse) : E растёт как V с точностью до постоянного множителя,
то есть E ≪ V² (E = 100·V - всё ещё разреженный)
плотный (dense) : E ≈ V² - почти все пары соединены
Примеры из жизни. Дорожная сеть страны: у города редко больше десятка выездов, значит E ≈ 5·V -
разреженный. Граф «кто кому звонил» у оператора связи - разреженный. Матрица расстояний между
всеми городами (полный граф) - плотный по определению. Социальная сеть - разреженная, хотя отдельные
вершины огромной степени там есть.
Почему это важнее, чем кажется: для V = 10⁶ разреженный граф - это порядка 10⁶ рёбер, их можно
держать в памяти и обойти за секунды; плотный - 10¹² рёбер, которые не влезут ни в какую память.
Алгоритм со сложностью O(V²) на миллионе вершин недопустим, а O(E) - вполне. Поэтому в оценках
графовых алгоритмов ВСЕГДА пишут и V, и E отдельно: O(V + E) и O(V²) - это разные миры,
хотя на плотном графе они совпадают.
Разбор: одна задача, три разных графа
Возьмём шахматного коня и вопрос «за сколько ходов доберётся с a1 на h8». Что здесь вершина, а что ребро? Вершина - КЛЕТКА (их 64), ребро - возможный ход конём между клетками. Рёбер получается около 168 (у центральных клеток по 8 ходов, у угловых по 2), граф неориентированный (конь ходит в обе стороны) и невзвешенный (каждый ход равноценен). Ответ даст обход в ширину.
Теперь зависимости пакетов: вершина - пакет, ребро A → B означает «A требует B». Граф
ОРИЕНТИРОВАННЫЙ, и его правильность - вопрос отсутствия циклов: если A требует B, B требует
C, а C требует A, установить нельзя ничего. Порядок установки даст топологическая сортировкаРазбирается в разделе 10, глава «Топологическая сортировка и компоненты сильной связности»: Топологическая сортировка: порядок, в котором можно делать.
И третий: лабиринт из клеток со стенами. Вершина - проходимая клетка, ребро - соседство без стены. Такой граф обычно НЕ хранят: он задан правилом «соседние клетки, если между ними нет стены», и соседей вычисляют на ходу. Это называют неявным графом, и он встречается чаще явных.
Заметь, что в трёх задачах менялось не решение, а МОДЕЛЬ: что назвать вершиной. Половина работы с графами - именно этот выбор, и он не всегда очевиден: в задачах про состояния вершиной бывает целое состояние системы (расстановка фигур, набор включённых ламп, позиция в игре).
Что ломается без этого
Первое: путать вершины и рёбра в оценках. O(V + E) и O(V·E) отличаются катастрофически, а в
разговоре «линейный обход графа» означает именно первое. Второе: считать граф связным по
умолчанию. Обход из одной вершины найдёт только её компоненту; если в графе несколько компонент,
обход надо запускать из каждой непосещённой вершины, иначе половина данных останется
необработанной. Третье: забыть про ориентированность. В ориентированном графе достижимость
несимметрична: из a в b дойти можно, из b в a - нет; алгоритм, написанный для
неориентированного, даст неверный ответ молча. Четвёртое: петли и кратные рёбра. Многие алгоритмы
их допускают, но некоторые ломаются (проверка двудольности на петле, формулы степеней), и на
реальных данных они появляются неожиданно - из дублей в базе. Пятое: думать, что граф всегда
маленький. E растёт квадратично, и представление, годное для тысячи вершин, не годится для
миллиона.
Зачем это в работе
Графы - самая универсальная модель в прикладном программировании, и знать их язык нужно уже затем,
чтобы УВИДЕТЬ граф в своей задаче. Сборка проекта - граф зависимостей между целями (и make
выполняет по нему топологическую сортировку). Пакетные менеджеры, миграции базы, планировщики задач
в CI - там же. Маршруты в навигаторе и в логистике - кратчайшие пути. Сети: маршрутизация пакетов,
поиск узла, отказ которого разорвёт сеть. Соцсети: рекомендации, поиск сообществ, степени знакомства.
Компиляторы: граф потока управления, граф вызовов, распределение регистров через раскраску. Базы
данных: планы запросов, блокировки и поиск взаимных блокировок - это поиск цикла. Машинное обучение:
вычислительный граф модели. В этом разделе мы разберём, как граф хранить и как его обходить; всё
остальное - надстройки над обходом.
Теперь сам
В социальной сети 10 миллионов пользователей, у каждого в среднем 200 друзей. Сколько в графе вершин и рёбер? Разреженный он или плотный? Сколько рёбер было бы в плотном графе такого же размера?
Ответ. Вершин V = 10⁷. Каждая дружба - ОДНО ребро, но считается у обоих концов, поэтому сумма
степеней равна 2E: 2E = 10⁷ × 200, откуда E = 10⁹ рёбер (миллиард). Разреженный он или
плотный? Сравним с максимумом: V²/2 = (10⁷)²/2 = 5·10¹³. Наш миллиард - это две стотысячных от
максимума, то есть граф разреженный, несмотря на впечатляющий миллиард рёбер. Плотный граф на
тех же вершинах имел бы 5·10¹³ рёбер - в пятьдесят тысяч раз больше, и хранить его невозможно
(даже по байту на ребро это 50 терабайт). Вывод, ради которого считали: «много рёбер» и «плотный» -
разные вещи; плотность измеряется отношением к V², и почти все большие графы из реальной жизни
разреженные. Именно поэтому дальше мы будем так придирчиво выбирать представление: на разреженном
графе матрица смежности из V² ячеек потратит 10¹⁴ ячеек ради миллиарда рёбер.
Главное
Граф - это вершины (V штук) и рёбра (E штук), соединяющие пары вершин. Ребро бывает
ориентированным (односторонним) и взвешенным. Словарь, который нужен дальше: степень вершины,
путь, простой путь, цикл, связность, компонента связности, дерево (связный граф без циклов, ровно
V − 1 рёбер). Дерево - частный случай графа, поэтому обходы из главы про деревья перенесутся
сюда почти без изменений. Границы: 0 <= E <= V·(V−1)/2 в простом неориентированном графе, то
есть E = O(V²); граф называют разреженным при E ≈ V и плотным при E ≈ V². Почти все большие
графы реальной жизни разреженные, и это определяет и выбор представления, и то, какие оценки
приемлемы: O(V + E) на миллионе вершин - секунды, O(V²) - невозможно. Половина работы с
графами - решить, что называть вершиной: клетку доски, пакет, состояние системы; модель выбирают
под вопрос, а дальше работают одни и те же алгоритмы.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий