kopia lustrzana https://github.com/villares/sketch-a-day
14a graphs!
rodzic
4e38121f86
commit
5d93dcad17
|
|
@ -0,0 +1,262 @@
|
|||
#*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
A simple Python graph class, demonstrating the essential facts and functionalities of graphs
|
||||
based on https://www.python-course.eu/graphs_python.php and https://www.python.org/doc/essays/graphs/
|
||||
"""
|
||||
|
||||
class Graph(object):
|
||||
|
||||
def __init__(self, graph_dict=None):
|
||||
"""
|
||||
Initialize a graph object with dictionary provided,
|
||||
if none provided, create an empty one.
|
||||
"""
|
||||
if graph_dict is None:
|
||||
graph_dict = {}
|
||||
self.__graph_dict = graph_dict
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__graph_dict)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.__graph_dict.keys())
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.__graph_dict[i]
|
||||
|
||||
def vertices(self):
|
||||
"""Return the vertices of graph."""
|
||||
return list(self.__graph_dict.keys())
|
||||
|
||||
def edges(self):
|
||||
"""Return the edges of graph """
|
||||
return self.__generate_edges()
|
||||
|
||||
def add_vertex(self, vertex):
|
||||
"""
|
||||
If the vertex "vertex" is not in self.__graph_dict,
|
||||
add key "vertex" with an empty list as a value,
|
||||
otherwise, do nothing.
|
||||
"""
|
||||
if vertex not in self.__graph_dict:
|
||||
self.__graph_dict[vertex] = []
|
||||
|
||||
def add_edge(self, edge):
|
||||
"""
|
||||
Assuming that edge is of type set, tuple or list;
|
||||
add edge between vertices. Can add multiple edges!
|
||||
"""
|
||||
edge = set(edge)
|
||||
vertex1 = edge.pop()
|
||||
if edge:
|
||||
# not a loop
|
||||
vertex2 = edge.pop()
|
||||
else:
|
||||
# a loop
|
||||
vertex2 = vertex1
|
||||
if vertex1 in self.__graph_dict:
|
||||
self.__graph_dict[vertex1].append(vertex2)
|
||||
else:
|
||||
self.__graph_dict[vertex1] = [vertex2]
|
||||
|
||||
def __generate_edges(self):
|
||||
"""
|
||||
Generate the edges, represented as sets with one
|
||||
(a loop back to the vertex) or two vertices.
|
||||
"""
|
||||
edges = []
|
||||
for vertex in self.__graph_dict:
|
||||
for neighbour in self.__graph_dict[vertex]:
|
||||
if {neighbour, vertex} not in edges:
|
||||
edges.append({vertex, neighbour})
|
||||
return edges
|
||||
|
||||
def __str__(self):
|
||||
res = "vertices: "
|
||||
for k in self.__graph_dict:
|
||||
res += str(k) + " "
|
||||
res += "\nedges: "
|
||||
for edge in self.__generate_edges():
|
||||
res += str(edge) + " "
|
||||
return res
|
||||
|
||||
def find_isolated_vertices(self):
|
||||
"""
|
||||
Return a list of isolated vertices.
|
||||
"""
|
||||
graph = self.__graph_dict
|
||||
isolated = []
|
||||
for vertex in graph:
|
||||
print(isolated, vertex)
|
||||
if not graph[vertex]:
|
||||
isolated += [vertex]
|
||||
return isolated
|
||||
|
||||
def find_path(self, start_vertex, end_vertex, path=[]):
|
||||
"""
|
||||
Find a path from start_vertex to end_vertex in graph.
|
||||
"""
|
||||
graph = self.__graph_dict
|
||||
path = path + [start_vertex]
|
||||
if start_vertex == end_vertex:
|
||||
return path
|
||||
if start_vertex not in graph:
|
||||
return None
|
||||
for vertex in graph[start_vertex]:
|
||||
if vertex not in path:
|
||||
extended_path = self.find_path(vertex,
|
||||
end_vertex,
|
||||
path)
|
||||
if extended_path:
|
||||
return extended_path
|
||||
return None
|
||||
|
||||
def find_all_paths(self, start_vertex, end_vertex, path=[]):
|
||||
"""
|
||||
Find all paths from start_vertex to end_vertex.
|
||||
"""
|
||||
graph = self.__graph_dict
|
||||
path = path + [start_vertex]
|
||||
if start_vertex == end_vertex:
|
||||
return [path]
|
||||
if start_vertex not in graph:
|
||||
return []
|
||||
paths = []
|
||||
for vertex in graph[start_vertex]:
|
||||
if vertex not in path:
|
||||
extended_paths = self.find_all_paths(vertex,
|
||||
end_vertex,
|
||||
path)
|
||||
for p in extended_paths:
|
||||
paths.append(p)
|
||||
return paths
|
||||
|
||||
def is_connected(self,
|
||||
vertices_encountered=None,
|
||||
start_vertex=None):
|
||||
"""Find if the graph is connected."""
|
||||
if vertices_encountered is None:
|
||||
vertices_encountered = set()
|
||||
gdict = self.__graph_dict
|
||||
vertices = list(gdict.keys()) # "list" necessary in Python 3
|
||||
if not start_vertex:
|
||||
# chosse a vertex from graph as a starting point
|
||||
start_vertex = vertices[0]
|
||||
vertices_encountered.add(start_vertex)
|
||||
if len(vertices_encountered) != len(vertices):
|
||||
for vertex in gdict[start_vertex]:
|
||||
if vertex not in vertices_encountered:
|
||||
if self.is_connected(vertices_encountered, vertex):
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def vertex_degree(self, vertex):
|
||||
"""
|
||||
Return the number of edges connecting to a vertex (the number of adjacent vertices).
|
||||
Loops are counted double, i.e. every occurence of vertex in the list of adjacent vertices.
|
||||
"""
|
||||
adj_vertices = self.__graph_dict[vertex]
|
||||
degree = len(adj_vertices) + adj_vertices.count(vertex)
|
||||
return degree
|
||||
|
||||
def degree_sequence(self):
|
||||
"""Calculates the degree sequence."""
|
||||
seq = []
|
||||
for vertex in self.__graph_dict:
|
||||
seq.append(self.vertex_degree(vertex))
|
||||
seq.sort(reverse=True)
|
||||
return tuple(seq)
|
||||
|
||||
@staticmethod
|
||||
def is_degree_sequence(sequence):
|
||||
"""
|
||||
Return True, if the sequence is a degree sequence (non-increasing),
|
||||
otherwise return False.
|
||||
"""
|
||||
return all(x >= y for x, y in zip(sequence, sequence[1:]))
|
||||
|
||||
def delta(self):
|
||||
"""Find minimum degree of vertices."""
|
||||
min = 100000000
|
||||
for vertex in self.__graph_dict:
|
||||
vertex_degree = self.vertex_degree(vertex)
|
||||
if vertex_degree < min:
|
||||
min = vertex_degree
|
||||
return min
|
||||
|
||||
def Delta(self):
|
||||
"""Finde maximum degree of vertices."""
|
||||
max = 0
|
||||
for vertex in self.__graph_dict:
|
||||
vertex_degree = self.vertex_degree(vertex)
|
||||
if vertex_degree > max:
|
||||
max = vertex_degree
|
||||
return max
|
||||
|
||||
def density(self):
|
||||
"""Calculate the graph density."""
|
||||
g = self.__graph_dict
|
||||
V = len(g.keys())
|
||||
E = len(self.edges())
|
||||
return 2.0 * E / (V * (V - 1))
|
||||
|
||||
def diameter(self):
|
||||
"""Calculates the graph diameter."""
|
||||
|
||||
v = self.vertices()
|
||||
pairs = [
|
||||
(v[i],
|
||||
v[j]) for i in range(
|
||||
len(v)) for j in range(
|
||||
i + 1,
|
||||
len(v) - 1)]
|
||||
smallest_paths = []
|
||||
for (s, e) in pairs:
|
||||
paths = self.find_all_paths(s, e)
|
||||
smallest = sorted(paths, key=len)[0]
|
||||
smallest_paths.append(smallest)
|
||||
|
||||
smallest_paths.sort(key=len)
|
||||
|
||||
# longest path is at the end of list,
|
||||
# i.e. diameter corresponds to the length of this path
|
||||
diameter = len(smallest_paths[-1]) - 1
|
||||
return diameter
|
||||
|
||||
@staticmethod
|
||||
def erdoes_gallai(dsequence):
|
||||
"""
|
||||
Check if Erdoes-Gallai inequality condition is fullfilled.
|
||||
"""
|
||||
if sum(dsequence) % 2:
|
||||
# sum of sequence is odd
|
||||
return False
|
||||
if Graph.is_degree_sequence(dsequence):
|
||||
for k in range(1, len(dsequence) + 1):
|
||||
left = sum(dsequence[:k])
|
||||
right = k * (k - 1) + sum([min(x, k) for x in dsequence[k:]])
|
||||
if left > right:
|
||||
return False
|
||||
else:
|
||||
# sequence is increasing
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Code by Eryk Kopczyński
|
||||
def find_shortest_path(self, start, end):
|
||||
from collections import deque
|
||||
graph = self.__graph_dict
|
||||
dist = {start: [start]}
|
||||
q = deque(start)
|
||||
while len(q):
|
||||
at = q.popleft()
|
||||
for next in graph[at]:
|
||||
if next not in dist:
|
||||
#dist[next] = [dist[at], next]
|
||||
dist[next] = dist[at]+[next] # less efficient but nicer output
|
||||
q.append(next)
|
||||
return dist.get(end)
|
||||
Plik binarny nie jest wyświetlany.
|
Po Szerokość: | Wysokość: | Rozmiar: 7.5 KiB |
|
|
@ -0,0 +1,66 @@
|
|||
from graph import Graph
|
||||
|
||||
graph = Graph({"g": ["d", "f"],
|
||||
"i": ["c"],
|
||||
"c": ["i", "c", "d", "e"],
|
||||
"d": ["f", "c"],
|
||||
"e": ["c"],
|
||||
"f": ["d", "h"],
|
||||
"a": ["b"],
|
||||
"h": ["f"],
|
||||
"b": ["a"]
|
||||
})
|
||||
|
||||
|
||||
def setup():
|
||||
size(500, 500)
|
||||
noLoop()
|
||||
setup_graph(graph)
|
||||
fill(0)
|
||||
textSize(18)
|
||||
textAlign(CENTER, CENTER)
|
||||
stroke(255)
|
||||
strokeWeight(3)
|
||||
|
||||
def draw():
|
||||
for v in grid.keys():
|
||||
xa, ya = grid[v]
|
||||
for e in graph[v]:
|
||||
xb, yb = grid[e]
|
||||
line(xa, ya, xb, yb)
|
||||
|
||||
for v in grid.keys():
|
||||
x, y = grid[v]
|
||||
text(v, x, y)
|
||||
|
||||
saveFrame("sketch_2020_07_14a.png")
|
||||
|
||||
def setup_graph(g):
|
||||
global cols, rows, grid
|
||||
cols, rows = dimensionar_grade(len(g))
|
||||
w, h = width / cols, height / rows
|
||||
grid = {}
|
||||
v_list = g.vertices()
|
||||
for c in range(cols):
|
||||
for r in range(rows):
|
||||
if v_list:
|
||||
v = v_list.pop()
|
||||
grid[v] = (w /2 + c * w,
|
||||
h /2 + r * h)
|
||||
|
||||
|
||||
def dimensionar_grade(n):
|
||||
a = int(sqrt(n))
|
||||
b = n / a
|
||||
if a * b < n:
|
||||
b += 1
|
||||
print(u'{}: {} × {} ({})'.format(n, a, b, a * b))
|
||||
return a, b
|
||||
|
||||
def keyPressed():
|
||||
global n
|
||||
redraw()
|
||||
if str(key) in '+=':
|
||||
n += 1
|
||||
if key == '-' and n > 2:
|
||||
n -= 1
|
||||
Ładowanie…
Reference in New Issue