This commit is contained in:
Dmitry Belyaev 2024-04-25 10:43:01 +03:00
parent e96d20343b
commit c22ef9e724
1 changed files with 90 additions and 84 deletions

View File

@ -12,7 +12,8 @@ Link - для описания связи между двумя произвол
LinkedGraph - для представления связного графа в целом (карта целиком).
Объекты класса Vertex должны создаваться командой:
v = Vertex()
>>> v = Vertex()
и содержать локальный атрибут:
_links - список связей с другими вершинами графа (список объектов класса Link).
@ -20,7 +21,9 @@ _links - список связей с другими вершинами граф
links - для получения ссылки на список _links.
Объекты следующего класса Link должны создаваться командой:
link = Link(v1, v2)
>>> v1 = Vertex(); v2 = Vertex()
>>> link = Link(v1, v2)
где v1, v2 - объекты класса Vertex (вершины графа). Внутри каждого объекта класса Link должны формироваться следующие локальные атрибуты:
_v1, _v2 - ссылки на объекты класса Vertex, которые соединяются данной связью;
@ -32,7 +35,7 @@ v2 - для получения ссылки на вершину v2;
dist - для изменения и считывания значения атрибута _dist.
Наконец, объекты третьего класса LinkedGraph должны создаваться командой:
map_graph = LinkedGraph()
>>> map_graph = LinkedGraph()
В каждом объекте класса LinkedGraph должны формироваться локальные атрибуты:
_links - список из всех связей графа (из объектов класса Link);
@ -58,30 +61,29 @@ _links = [Link(v1, v2)]
Пример использования классов, применительно к схеме метро (эти строчки в программе писать не нужно):
map_graph = LinkedGraph()
v1 = Vertex()
v2 = Vertex()
v3 = Vertex()
v4 = Vertex()
v5 = Vertex()
v6 = Vertex()
v7 = Vertex()
map_graph.add_link(Link(v1, v2))
map_graph.add_link(Link(v2, v3))
map_graph.add_link(Link(v1, v3))
map_graph.add_link(Link(v4, v5))
map_graph.add_link(Link(v6, v7))
map_graph.add_link(Link(v2, v7))
map_graph.add_link(Link(v3, v4))
map_graph.add_link(Link(v5, v6))
print(len(map_graph._links)) # 8 связей
print(len(map_graph._vertex)) # 7 вершин
path = map_graph.find_path(v1, v6)
>>> Vertex._num = 0
>>> map_graph = LinkedGraph()
>>> v1 = Vertex()
>>> v2 = Vertex()
>>> v3 = Vertex()
>>> v4 = Vertex()
>>> v5 = Vertex()
>>> v6 = Vertex()
>>> v7 = Vertex()
>>> map_graph.add_link(Link(v1, v2))
>>> map_graph.add_link(Link(v2, v3))
>>> map_graph.add_link(Link(v1, v3))
>>> map_graph.add_link(Link(v4, v5))
>>> map_graph.add_link(Link(v6, v7))
>>> map_graph.add_link(Link(v2, v7))
>>> map_graph.add_link(Link(v3, v4))
>>> map_graph.add_link(Link(v5, v6))
>>> len(map_graph._links)
8
>>> len(map_graph._vertex)
7
>>> map_graph.find_path(v1, v6)
([Vertex('A'), Vertex('B'), Vertex('G'), Vertex('F')], [Link(Vertex('A'), Vertex('B'), 1), Link(Vertex('B'), Vertex('G'), 1), Link(Vertex('F'), Vertex('G'), 1)])
Однако, в таком виде применять классы для схемы карты метро не очень удобно.
Например, здесь нет указаний названий станций, а также длина каждого сегмента равна 1, что не соответствует действительности.
@ -91,7 +93,8 @@ class Station(Vertex): ... - для описания станций метро;
class LinkMetro(Link): ... - для описания связей между станциями метро.
Объекты класса Station должны создаваться командой:
st = Station(name)
>>> st = Station(name := "Домодедовская")
где name - название станции (строка). В каждом объекте класса Station должен дополнительно формироваться локальный атрибут:
name - название станции метро.
@ -99,37 +102,42 @@ name - название станции метро.
В самом классе Station переопределите магические методы __str__() и __repr__(), чтобы они возвращали название станции метро (локальный атрибут name).
Объекты второго класса LinkMetro должны создаваться командой:
link = LinkMetro(v1, v2, dist)
>>> link = LinkMetro(v1, v2, dist := 2)
где v1, v2 - вершины (станции метро); dist - расстояние между станциями (любое положительное число).
(Также не забывайте в инициализаторе этого дочернего класса вызывать инициализатор базового класса).
В результате, эти классы должны совместно работать следующим образом (эти строчки в программе писать не нужно):
map_metro = LinkedGraph()
v1 = Station("Сретенский бульвар")
v2 = Station("Тургеневская")
v3 = Station("Чистые пруды")
v4 = Station("Лубянка")
v5 = Station("Кузнецкий мост")
v6 = Station("Китай-город 1")
v7 = Station("Китай-город 2")
>>> map_metro = LinkedGraph()
>>> v1 = Station("Сретенский бульвар")
>>> v2 = Station("Тургеневская")
>>> v3 = Station("Чистые пруды")
>>> v4 = Station("Лубянка")
>>> v5 = Station("Кузнецкий мост")
>>> v6 = Station("Китай-город 1")
>>> v7 = Station("Китай-город 2")
map_metro.add_link(LinkMetro(v1, v2, 1))
map_metro.add_link(LinkMetro(v2, v3, 1))
map_metro.add_link(LinkMetro(v1, v3, 1))
>>> map_metro.add_link(LinkMetro(v1, v2, 1))
>>> map_metro.add_link(LinkMetro(v2, v3, 1))
>>> map_metro.add_link(LinkMetro(v1, v3, 1))
map_metro.add_link(LinkMetro(v4, v5, 1))
map_metro.add_link(LinkMetro(v6, v7, 1))
>>> map_metro.add_link(LinkMetro(v4, v5, 1))
>>> map_metro.add_link(LinkMetro(v6, v7, 1))
map_metro.add_link(LinkMetro(v2, v7, 5))
map_metro.add_link(LinkMetro(v3, v4, 3))
map_metro.add_link(LinkMetro(v5, v6, 3))
>>> map_metro.add_link(LinkMetro(v2, v7, 5))
>>> map_metro.add_link(LinkMetro(v3, v4, 3))
>>> map_metro.add_link(LinkMetro(v5, v6, 3))
print(len(map_metro._links))
print(len(map_metro._vertex))
path = map_metro.find_path(v1, v6) # от сретенского бульвара до китай-город 1
print(path[0]) # [Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1]
print(sum([x.dist for x in path[1]])) # 7
>>> print(len(map_metro._links))
8
>>> print(len(map_metro._vertex))
7
>>> path = map_metro.find_path(v1, v6) # от сретенского бульвара до китай-город 1
>>> print(path[0]) # [Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1]
[Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1]
>>> print(sum([x.dist for x in path[1]])) # 7
7
P.S. В программе нужно объявить только классы Vertex, Link, LinkedGraph, Station, LinkMetro. На экран ничего выводить не нужно.
"""
@ -137,7 +145,7 @@ P.S. В программе нужно объявить только классы
from collections import defaultdict
from dataclasses import dataclass, field
from functools import total_ordering
from typing import List, Union
from typing import List, Optional, Tuple, Union
def make_properties_prot(*names):
@ -159,12 +167,14 @@ def make_properties_prot(*names):
class AutoNamed:
_num = 0
_num: int = 0
name: str
@staticmethod
def column_code(num):
def gen(n):
def column_code(num: int) -> str:
"""Имя как код колонки в Excel по порядковому номеру с 1"""
def gen(n: int):
a = ord("A")
sz = ord("Z") - a + 1
while n:
@ -182,7 +192,7 @@ class AutoNamed:
@make_properties_prot("links")
class Vertex(AutoNamed):
def __init__(self, name=None):
def __init__(self, name: Optional[str] = None):
if name:
self.name = name
self._links = []
@ -238,7 +248,7 @@ class LinksPath:
return sum([x.dist for x in self.links])
return float("inf")
def add_link(self, link):
def add_link(self, link: Link):
if link not in self.links:
self.links.append(link)
return self
@ -263,52 +273,51 @@ class LinksPath:
@make_properties_prot("links", "vertex")
class LinkedGraph:
def __init__(self, vertex=None, links=None):
def __init__(self, vertex: Optional[Vertex] = None, links: Optional[Link] = None):
self._vertex = vertex or []
self._links = links or []
def __repr__(self) -> str:
return f"{self.__class__.__name__}{(self.vertex, self.links)!r}"
def add_vertex(self, v):
def add_vertex(self, v: Vertex):
if v not in self._vertex:
self._vertex.append(v)
def add_link(self, link):
def add_link(self, link: Link):
if link not in self._links:
self._links.append(link)
for v in link:
self.add_vertex(v)
def dijkstras(self, start, stop=None):
def dijkstras(self, start: Vertex, stop: Optional[Vertex] = None):
def walk(remaining, paths, current):
while remaining:
remaining.discard(current)
yield current
if current == stop:
break
if remaining:
current = min(remaining, key=lambda x: paths[x])
paths = defaultdict(LinksPath)
paths[start].is_start = True
remaining, current = set(self.vertex), start
while remaining:
remaining.discard(current)
remaining = set(self.vertex)
for current in walk(remaining, paths, start):
for link in current:
for v in link:
if v not in remaining:
continue
for v in filter(lambda v: v in remaining, link):
new_path = paths[current].copy().add_link(link)
if new_path < paths[v]:
paths[v] = new_path
if current == stop:
break
if remaining:
current = min(remaining, key=lambda x: paths[x])
paths[v] = min(paths[v], new_path)
return paths
def find_path(self, start_v, stop_v):
def find_path(
self, start_v: Vertex, stop_v: Vertex
) -> Tuple[List[Vertex], List[Link]]:
path = self.dijkstras(start_v, stop_v)[stop_v]
return path.vertex, path.links
class Station(Vertex):
def __init__(self, name: str):
super().__init__()
self.name = name
def __repr__(self):
return self.name
@ -317,14 +326,9 @@ class LinkMetro(Link):
...
vA = Vertex("A")
vB = Vertex("B")
vC = Vertex("C")
vD = Vertex("D")
vE = Vertex("E")
vF = Vertex("F")
vG = Vertex("G")
print("-------------------------------------------")
vA, vB, vC, vD, vE, vF, vG = [Vertex() for _ in range(7)]
map_graph = LinkedGraph()
map_graph.add_link(Link(vA, vB))
map_graph.add_link(Link(vB, vC))
@ -340,6 +344,7 @@ map_graph.add_link(Link(vE, vF))
print(len(map_graph._links)) # 8 связей
print(len(map_graph._vertex)) # 7 вершин
print(map_graph.find_path(vA, vG))
# ([Vertex('A'), Vertex('B'), Vertex('G')], [Link(Vertex('A'), Vertex('B'), 1), Link(Vertex('B'), Vertex('G'), 1)])
print("-------------------------------------------")
@ -370,6 +375,7 @@ print(path[0]) # [Сретенский бульвар, Тургеневская
print(sum([x.dist for x in path[1]])) # 7
print("-------------------------------------------", flush=True)
Vertex._num = 0 # naming reset
# exit(1)