Реализация¶
Реализовать список смежности на Python с помощью словарей очень легко. Для АТД графа мы создадим два класса (см. листинг 1 и листинг 2): Graph , содержащий основной список вершин, и Vertex — представление каждой из них в графе.
Объекты Vertex будут использовать словарь для отслеживания смежных вершин и весов рёбер. Называться он будет connectedTo . Листинг ниже показывает код для класса Vertex . Конструктор просто инициализирует id (обычную строку) и словарь connectedTo . Метод addNeighbor используется для добавления связи данной вершины с другой. Метод getConnections возвращает все вершины из списка смежности, которые представлены в connectedTo . Метод getWeight возвращает вес ребра из этой вершины к передаваемой ему в качестве параметра.
Листинг 1
class Vertex: def __init__(self,key): self.id = key self.connectedTo = <> def addNeighbor(self,nbr,weight=0): self.connectedTo[nbr] = weight def __str__(self): return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo]) def getConnections(self): return self.connectedTo.keys() def getId(self): return self.id def getWeight(self,nbr): return self.connectedTo[nbr]
Класс Graph , показанный в следующем листинге, содержит словарь, отображающий имена вершин на их объекты. На рисунке 4 последний показан затенённым серым прямоугольником. Также Graph предоставляет методы для добавления вершин в граф и связывания их друг с другом. Дополнительно мы имеем реализацию метода __iter__ , облегчающего итерации по объектам Vertex в конкретном графе. Вместе два метода позволяют делать итерации по именам вершин или непосредственно по объектам.
Листинг 2
class Graph: def __init__(self): self.vertList = <> self.numVertices = 0 def addVertex(self,key): self.numVertices = self.numVertices + 1 newVertex = Vertex(key) self.vertList[key] = newVertex return newVertex def getVertex(self,n): if n in self.vertList: return self.vertList[n] else: return None def __contains__(self,n): return n in self.vertList def addEdge(self,f,t,cost=0): if f not in self.vertList: nv = self.addVertex(f) if t not in self.vertList: nv = self.addVertex(t) self.vertList[f].addNeighbor(self.vertList[t], cost) def getVertices(self): return self.vertList.keys() def __iter__(self): return iter(self.vertList.values())
Следующая сессия Python создаёт граф с рисунка 2, используя свежеопределённые классы Graph и Vertex . Сначала мы создаём шесть вершин, пронумерованных от 0 до 5. Затем печатаем словарь, их содержащий. Обратите внимание, что для каждого ключа от 0 до 5 создаётся сущность класса Vertex . Далее добавляем рёбра, связывающие вершины вместе. Наконец, вложенный цикл удостоверяется, что каждое ребро в графе сохранено правильно. Вы можете сравнить вывод списка рёбер в конце сессии с рисунком 2.
>>> g = Graph() >>> for i in range(6): . g.addVertex(i) >>> g.vertList , 1: , 2: , 3: , 4: , 5: > >>> g.addEdge(0,1,5) >>> g.addEdge(0,5,2) >>> g.addEdge(1,2,4) >>> g.addEdge(2,3,9) >>> g.addEdge(3,4,7) >>> g.addEdge(3,5,3) >>> g.addEdge(4,0,1) >>> g.addEdge(5,4,8) >>> g.addEdge(5,2,1) >>> for v in g: . for w in v.getConnections(): . print("( %s , %s )" % (v.getId(), w.getId())) . ( 0 , 5 ) ( 0 , 1 ) ( 1 , 2 ) ( 2 , 3 ) ( 3 , 4 ) ( 3 , 5 ) ( 4 , 0 ) ( 5 , 4 ) ( 5 , 2 )
readers online now | | Back to top
© Copyright 2014 Brad Miller, David Ranum. Created using Sphinx 1.2.3.
Простой способ решать графы на питоне: пошаговый гид для начинающих
Для решения задач, связанных с графами, вам понадобятся некоторые основные концепции и алгоритмы. Вот несколько шагов, которые помогут вам начать работу. 1. Создайте граф:
# Импорт библиотеки networkx import networkx as nx # Создание пустого графа G = nx.Graph() # Добавление вершин G.add_node(1) G.add_node(2) # Добавление ребер G.add_edge(1, 2)
2. Выполните задачи над графом:
# Проверка, существует ли вершина в графе if G.has_node(1): print("Вершина 1 существует в графе") # Проверка, существует ли ребро между вершинами if G.has_edge(1, 2): print("Ребро между вершинами 1 и 2 существует") # Получение списка вершин и ребер графа nodes = G.nodes() edges = G.edges() print("Список вершин:", nodes) print("Список ребер:", edges)
3. Примените алгоритмы к графу:
# Нахождение кратчайшего пути между двумя вершинами shortest_path = nx.shortest_path(G, source=1, target=2) print("Кратчайший путь между вершинами 1 и 2:", shortest_path) # Выполнение обхода в ширину графа bfs = nx.bfs_tree(G, source=1) print("Обход в ширину графа:", bfs.edges()) # Выполнение обхода в глубину графа dfs = nx.dfs_tree(G, source=1) print("Обход в глубину графа:", dfs.edges())
Это лишь небольшой обзор возможностей работы с графами на языке Python. Ответить на все ваши вопросы в одной статье невозможно, но вы можете продолжить изучение и использование библиотеки NetworkX для работы с графами на Python.
Детальный ответ
Как решать графы на питоне
Решение задач, связанных с графами, является важной частью программирования на Python. Графы — это абстрактные структуры данных, состоящие из вершин и ребер, которые связывают эти вершины между собой. В этой статье мы рассмотрим основные подходы и инструменты, которые помогут вам решить задачи, связанные с графами на питоне.
Представление графов
Для начала, нам необходимо определиться с представлением графа. Существует несколько способов представления графов в питоне, но два наиболее распространенных подхода — это использование матрицы смежности и списков смежности.
# Пример представления графа с помощью матрицы смежности graph = [[0, 1, 0], [1, 0, 1], [0, 1, 0]] # Пример представления графа с помощью списков смежности graph =
Матрица смежности представляет граф в виде квадратной матрицы, где значение 1 указывает на наличие ребра между двумя вершинами, а значение 0 — на его отсутствие. Списки смежности представляют граф в виде словаря, где каждой вершине сопоставлен список вершин, с которыми она смежна.
Обход графа
Первым шагом при работе с графом часто становится необходимость выполнить его обход. Существует несколько алгоритмов обхода графа, наиболее популярными из которых являются алгоритмы поиска в глубину (Depth-First Search, DFS) и поиска в ширину (Breadth-First Search, BFS). DFS — это алгоритм, который идет «вглубь» графа до тех пор, пока не достигнет конечной вершины. Вот пример реализации алгоритма DFS:
def dfs(graph, start, visited): visited.add(start) print(start, end=' ') for neighbor in graph[start]: if neighbor not in visited: dfs(graph, neighbor, visited) # Пример использования DFS graph = < 0: [1, 2], 1: [2], 2: [0, 3], 3: [3] >visited = set() dfs(graph, 2, visited) # Вывод: 2 0 1 3
BFS — это алгоритм, который идет «вширь» графа, исследуя все вершины на одном уровне, прежде чем переходить на следующий уровень. Вот пример реализации алгоритма BFS:
from collections import deque def bfs(graph, start): visited = set() queue = deque([start]) visited.add(start) while queue: vertex = queue.popleft() print(vertex, end=' ') for neighbor in graph[vertex]: if neighbor not in visited: visited.add(neighbor) queue.append(neighbor) # Пример использования BFS graph = < 0: [1, 2], 1: [2], 2: [0, 3], 3: [3] >bfs(graph, 2) # Вывод: 2 0 3 1
Поиск кратчайшего пути
Еще одной важной задачей при работе с графами является поиск кратчайшего пути между двумя вершинами. Для этого часто используются алгоритмы Дейкстры и алгоритм A*. Алгоритм Дейкстры находит кратчайший путь из начальной вершины до всех остальных вершин во взвешенном графе. Вот пример реализации алгоритма Дейкстры:
import heapq def dijkstra(graph, start): distances = distances[start] = 0 heap = [(0, start)] while heap: current_distance, current_vertex = heapq.heappop(heap) if current_distance > distances[current_vertex]: continue for neighbor, weight in graph[current_vertex].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(heap, (distance, neighbor)) return distances # Пример использования алгоритма Дейкстры graph = < 'A': , 'B': , 'C': , 'D': > distances = dijkstra(graph, 'A') print(distances) # Вывод:
Алгоритм A* — это эффективный алгоритм поиска кратчайшего пути в графе с заданными начальной и конечной вершинами. Этот алгоритм часто используется для поиска путей в компьютерных играх и робототехнике. Пример реализации алгоритма A* выходит за рамки данной статьи.
Вывод
В этой статье мы рассмотрели основные подходы и инструменты, которые помогут вам решить задачи, связанные с графами на питоне. Вы узнали о различных способах представления графов, алгоритмах обхода графа, а также алгоритмах поиска кратчайшего пути. Используйте эти знания, чтобы успешно решать задачи, связанные с графами, ваши навыки программирования на питоне непременно улучшатся!
работа с графами в Python
Будет использоваться ненаправленный связный граф V=6 E=6. Существует две популярные методики представления графов: матрица смежности (эффективна с плотными графами) и список связей (эффективно с разряженными графами). Будем использовать второй способ.
graph = 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E']>
2 Depth-First Search — Поиск вглубину
Алгоритм поиска вглубину: исследуем сначала все возможные вершины (из выбранного корня) доступные из текущей, прежде чем возвращаться назад. Данный алгоритм можно реализовать как рекурсивно, так и итеративно. Последовательность действий:
- Помечаем текущую вершину как посещённую
- Исследуем каждую соседнюю вершину не включённую в список уже посещённых
- Вариант с DFS and BFS in Python (модифицированный, т.к. set не поддерживает упорядоченность элементов)
graph = 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E']> def dfs(graph, start): visited, stack = [], [start] while stack: vertex = stack.pop() if vertex not in visited: visited.append(vertex) stack.extend(set(graph[vertex]) - set(visited)) return visited print(dfs(graph, 'A'))
['A', 'C', 'F', 'E', 'B', 'D']
- Вариант с DFS and BFS graph traversal (Python recipe) (модифицированный, т.к. для реализации стека нам необходимо добавлять элементы в конец списка, а не в начало)
graph = 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E']> def iteractive_dfs(graph, start, path=None): """iterative depth first search from start""" if path is None: path = [] q = [start] while q: v = q.pop() if v not in path: path = path + [v] q += graph[v] return path print(iteractive_dfs(graph, 'A'))
['A', 'C', 'F', 'E', 'B', 'D']
3 DFS Paths — поиск пути между двумя вершинами
graph = 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E']> def dfs_paths(graph, start, goal): stack = [(start, [start])] # (vertex, path) while stack: (vertex, path) = stack.pop() for next in set(graph[vertex]) - set(path): if next == goal: yield path + [next] else: stack.append((next, path + [next])) print(list(dfs_paths(graph, 'A', 'F')))
[['A', 'C', 'F'], ['A', 'B', 'E', 'F']]
4 Bread-Firsth Search — Поиск вширину
Позволяет найти кратчайший путь между двумя вершинами. Довольно сложно реализовать рекурсивно, гораздо проще реализовать его с использованием очереди.
graph = 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E']> from queue import deque def bfs(graph, start): visited, queue = [], deque([start]) while queue: vertex = queue.pop() if vertex not in visited: visited.append(vertex) queue.extendleft(set(graph[vertex]) - set(visited)) return visited print(bfs(graph, 'A'))
['A', 'B', 'C', 'D', 'E', 'F']
5 BFS Paths
from queue import deque graph = 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E']> def bfs_paths(graph, start, goal): queue = deque([(start, [start])]) while queue: (vertex, path) = queue.pop() for next in set(graph[vertex]) - set(path): if next == goal: yield path + [next] else: queue.appendleft((next, path+[next])) print(list(bfs_paths(graph, 'A', 'F'))) def shortest_path(graph, start, goal): try: return next(bfs_paths(graph, start, goal)) except StopIteration: return None print(shortest_path(graph, 'A', 'F')) print(shortest_path(graph, 'E', 'D')) print(shortest_path(graph, 'A', 'D')) print(shortest_path(graph, 'F', 'D'))
[['A', 'C', 'F'], ['A', 'B', 'E', 'F']] ['A', 'C', 'F'] ['E', 'B', 'D'] ['A', 'B', 'D'] ['F', 'E', 'B', 'D']
Author: Pavel Vavilin
Created: 2017-11-09 Thu 19:40
5 графов, которые должен знать каждый Data Scientist

Если вы анализируете поведение пользователей, выстраиваете сети или решаете другие динамические задачи, вам не обойтись без структур, которые специально созданы для визуализации взаимоотношений между различными объектами. Речь, разумеется, о графах. Рассказываем про пять разных типов графов: как они применяются в Data Science и как реализованы в Python.

Освойте профессию «Data Scientist»
Графы в Python
Граф состоит из отдельных точек (вершин) и соединяющих их линий (ребер). Вершины могут обозначать любые объекты: пользователей сайта, компьютеры корпоративной сети, населенные пункты на карте. Ребра отображают связи между этими объектами: каких пользователей тот или иной человек добавил в друзья, какие компьютеры объединяются прямым подключением, между какими городами проложены дороги. Такая схема позволяет решать множество прикладных задач, и для каждой из них есть много подходящих графов.
Профессия / 24 месяца
Data Scientist
Дата-сайентисты решают поистине амбициозные задачи. Научитесь создавать искусственный интеллект, обучать нейронные сети, менять мир и при этом хорошо зарабатывать. Программа рассчитана на новичков и плавно введет вас в Data Science.
4 992 ₽/мес 9 983 ₽/мес

Такие структуры гораздо сложнее привычных таблиц баз данных. Представьте себе, что вам нужно создать карту значимых объектов обычного городского района, чтобы понять, где чаще всего бывают его жители. Вам нужно отметить все магазины, школы, МФЦ, и определить вес каждой из точки. Такой код непросто написать с нуля, а полученный результат очень сложно визуализировать в пригодном для работы формате — хаос повседневности формирует очень запутанные связи, даже если мы говорим про район, а не про город или страну. К счастью, Python-программистам не приходится заниматься подготовительной работой. Они могут воспользоваться специальной библиотекой, которая объединяет все необходимые инструменты для создания и анализа графов — NetworkX. Мы также используем этот пакет в следующих примерах.

Станьте дата-сайентистом и решайте амбициозные задачи с помощью нейросетей
1. Разбивка вершин на группы
Анализ связных компонентов (connected components) позволяет вам определить внутренние множества внутри вашего графа. Например, вы можете разбить посетителей интернет-магазина на группы по их предпочтениям или географической принадлежности. Или связать между собой подозрительные транзакции в банковской системе, чтобы предположить, что все эти действия провела одна группа мошенников.
Технически алгоритм объединяет в себе два метода, с помощью которых можно найти кратчайший путь между вершинами графа: поиск по ширине (Breadth First Search, BFS) и поиск по глубине (Depth First Search, DFS). Самые старательные ученики Data Science могут подробнее прочитать о них здесь, а мы сразу перейдем к коду. Допустим, вам нужно распределить города по континентам, используя информацию о расстоянии между ними. Исходный граф выглядит следующим образом:
Сначала мы создаем список граней графа, где расстояния будут использоваться в качестве весов:
Теперь строим граф с помощью Networkx:
Остается применить к полученной структуре алгоритм связных компонент:
Готово — мы получили три группы городов, объединенных по континентальному принципу. Эту модель можно применять для самых разных целей. Если бы вместо городов у нас были, например, идентификаторы пользователей, а вместо расстояний — их траты в разных товарных категориях, алгоритм сгруппировал бы их по предпочтениям.
2. Поиск кратчайшего пути
Вычисление наименьшего расстояния между двумя точками — одна из старейших задач программирования. Один из наилучших алгоритмов для ее решения еще в конце 50-х разработал нидерландский математик Эдсгер Дейкстра (Edsger Dijkstra), причем человечество может благодарить за это открытие… его невесту. Как рассказывал сам Дейкстра, однажды его так утомили походы по магазинам вместе с молодой женой, что во время краткой передышки он задумался — как оптимизировать этот процесс? Буквально за 20 минут он разработал метод, который теперь называется алгоритмом Дейкстры. Метод прост, как все гениальное. Для начала Data Scientist узнает расстояния от начальной точки до каждой точки в пути. Эти значения присваиваются граням графа. Далее алгоритм поочередно проходит по каждой вершине, на каждом шаге определяя наименьшее расстояние до очередной точки. Когда все точки посещены, вы получаете наименьший маршрут. По словам самого математика, метод оказался так прост потому, что у него под рукой не было ни клочка бумаги — все калькуляции приходилось вести в голове. Такой способ не дает углубиться в вычисления, поэтому итоговое решение настолько просто, насколько это вообще возможно. Теперь о том, как алгоритм Дейкстры реализован в Python. Для решения задачи достаточно двух строчек кода:
Мы также можем найти кратчайшее расстояние между всеми парами:
В нашем примере мы определили маршруты между городами, но этот же метод можно использовать и для выстраивания связей между пользователями соцсети, и для планирования оптимального маршрута покупателя в торговом зале, и для любых прочих подобных проблем.
Читайте также 8 причин стать дата-сайентистом в 2023 году
3. Поиск оптимального пути
Всем знакомы головоломки, в которых нужно начертить какую-либо фигуру, не проходя дважды через одну точку. В профессии Data Scientist такие задачи возникают, когда нужно проложить оптимальный путь для кабеля, помочь торговым представителям или мастерам по обслуживанию банкоматов спланировать маршрут, и так далее. В каждом случае помогает алгоритм минимального остовного дерева (Minimum Spanning Tree). Этот термин обозначает подграф, который объединяет ребра с минимально возможной суммой весов. Это не всегда означает, что одна вершина не может иметь больше двух ребер — главное значение имеет итоговая сумма их значений.
Чтобы построить такую структуру, используйте команду: 
4. Рейтинг вершин графа
Еще одна нередкая задача — найти, какие вершины имеют самое важное значение в рамках того или иного явления. Например, поисковики определяют вес разных интернет-страниц, академики считают, какие научные работы чаще всего ссылаются исследователи, социальные сети отслеживают пользователей с наибольшим количеством подписок. На примере социальных сетей мы и рассмотрим метод. Достаем из хранилища данных список пользователей Фейсбук*:
И построим граф:
Теперь посмотрим, у каких пользователей наибольшее влияние. Алгоритм Pagerank умеет сам это делать, опираясь на количество онлайн-друзей:
Чтобы узнать пользователей с наибольшим весом, мы используем этот код:
А чтобы определить «самого крутого парня», можно построить подграф:
*деятельность компании Meta Platforms Inc., которой принадлежит Инстаграм / Фейсбук, запрещена на территории РФ в части реализации данной (-ых) социальной (-ых) сети (-ей) на основании осуществления ею экстремистской деятельности
5. Центральность вершин
Понятие центральности обозначает расстояние какой-либо вершины графа до его центра. Измерение центральности — это еще один способ определить влияние разных участников базы данных. В отличие от предыдущего метода, определение центральности направлено на выявление не самой главной вершины, а локальных «лидеров», которых может быть и пять, и десять. Таким образом Data Scientist может изучать распространение информации в сообществе (определять изначальное сообщение и следить за его распространением через несколько точек входа), отслеживать развитие болезней (строить карту заражений с нулевыми пациентами), контролировать телекоммуникационные и прочие сети. Существует множество метрик центральности. В нашем примере мы расскажем, как найти на графе вершины, которые чаще всего связывают между собой разрозненные группы точек. На практике это может быть перевалочный пункт на географическом маршруте, «бутылочное горлышко» в каких-либо процессах, человек-посредник, который знакомит между собой других людей. В теории графов это и называется степенью посредничества (Betweenness Centrality). Чтобы найти такие вершины, используйте следующий код:
Чем больше диаметр круга, тем больше его степень посредничества. Если устранить эти узлы из системы, большой граф разобьется на несколько малых. Мы рассмотрели всего пять графов, но даже такой краткий экскурс дает представление о том, какой это мощный инструмент Data Science. Изучайте графы, используйте их в своих проектах, и вы получите множество инсайтов о том, как работает наш постоянно меняющийся мир.
Data Scientist
Дата-сайентисты решают поистине амбициозные задачи. Научитесь создавать искусственный интеллект, обучать нейронные сети, менять мир и при этом хорошо зарабатывать. Программа рассчитана на новичков и плавно введет вас в Data Science.
