kopia lustrzana https://github.com/villares/sketch-a-day
				
				
				
			
			
			
			
				main
			
			
		
		
							rodzic
							
								
									7cbf07a8b8
								
							
						
					
					
						commit
						2f02fd4ff7
					
				|  | @ -0,0 +1,299 @@ | |||
| #*- 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/ | ||||
| """ | ||||
| 
 | ||||
| from random import choice | ||||
| 
 | ||||
| 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 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() | ||||
|             if vertex1 in self.__graph_dict: | ||||
|                 self.__graph_dict[vertex1].append(vertex2) | ||||
|             else: | ||||
|                 self.__graph_dict[vertex1] = [vertex2] | ||||
|             if vertex2 in self.__graph_dict: | ||||
|                 self.__graph_dict[vertex2].append(vertex1) | ||||
|             else: | ||||
|                 self.__graph_dict[vertex2] = [vertex1] | ||||
|         else: | ||||
|             # a loop | ||||
|             if vertex1 in self.__graph_dict: | ||||
|                 self.__graph_dict[vertex1].append(vertex1) | ||||
|             else: | ||||
|                 self.__graph_dict[vertex1] = [vertex1]         | ||||
| 
 | ||||
|     def remove_vertex(self, vert): | ||||
|         del self.__graph_dict[vert] | ||||
|         for k in self.__graph_dict.keys(): | ||||
|             if vert in self.__graph_dict[k]: | ||||
|                 self.__graph_dict[k].remove(vert) | ||||
|        | ||||
|     def remove_edge(self, edge): | ||||
|         edge = set(edge) | ||||
|         vertex1 = edge.pop() | ||||
|         if edge: | ||||
|             vertex2 = edge.pop() | ||||
|             self.__graph_dict[vertex1].remove(vertex2) | ||||
|             self.__graph_dict[vertex2].remove(vertex1) | ||||
|         else: | ||||
|             self.__graph_dict[vertex1].remove(vertex1) | ||||
| 
 | ||||
|     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) | ||||
|      | ||||
|      | ||||
|     def get_random_vertex(self): | ||||
|         return choice(self.vertices()) | ||||
|      | ||||
|     @staticmethod             | ||||
|     def random_graph(names, connect_rate=.9, allow_loops=True): | ||||
|         vertices = set(names) | ||||
|         graph = Graph() | ||||
|         for v in vertices: | ||||
|             graph.add_vertex(v) | ||||
|             if random(1) < connect_rate: | ||||
|                 if allow_loops: | ||||
|                     names = list(vertices) | ||||
|                 else: | ||||
|                     names = list(vertices - set((v,))) | ||||
|                 graph.add_edge({v, choice(names)})     | ||||
|         return graph | ||||
|  | @ -0,0 +1,64 @@ | |||
| #*- coding: utf-8 -*- | ||||
| 
 | ||||
| from __future__ import division, print_function | ||||
| 
 | ||||
| def setup_grid(graph, width, height, margin=None): | ||||
|     margin = margin or width / 40 | ||||
|     cols, rows = dim_grid(len(graph)) | ||||
|     w, h = (width - margin * 2) / cols, (height - margin * 2) / rows | ||||
|     points = [] | ||||
|     for i in range(cols * rows): | ||||
|         c = i % cols | ||||
|         r = i // rows | ||||
|         x = margin + w * 0.5 + c * w - 14 * (r % 2) + 7 | ||||
|         y = margin + h * 0.5 + r * h - 14 * (c % 2) + 7 | ||||
|         z = 0 | ||||
|         points.append((x, y, z)) | ||||
|     points = sorted( | ||||
|         points, key=lambda p: dist(p[0], p[1], width / 2, height / 2)) | ||||
|     v_list = reversed(sorted(graph.vertices(), key=graph.vertex_degree)) | ||||
|     # v_list = sorted(graph.vertices(), key=graph.vertex_degree) | ||||
|     grid = {v: p for v, p in zip(v_list, points)} | ||||
|     return grid | ||||
| 
 | ||||
| def dim_grid(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 edge_distances(graph, grid): | ||||
|     total = 0 | ||||
|     for edge in graph.edges(): | ||||
|         if len(edge) == 2: | ||||
|             a, b = edge | ||||
|             d = PVector.dist(PVector(*grid[a]), | ||||
|                              PVector(*grid[b])) | ||||
|             total += d | ||||
|     return total | ||||
| 
 | ||||
| def grid_swap(graph, grid, num=2): | ||||
|     from random import sample | ||||
|     fail = 0 | ||||
|     n = m = edge_distances(graph, grid) | ||||
|     while m <= n and fail < len(graph) ** 2: | ||||
|         new_grid= dict(grid) | ||||
|         if num == 2: | ||||
|             a, b = sample(graph.vertices(), 2)   | ||||
|             new_grid[a], new_grid[b] = new_grid[b], new_grid[a] | ||||
|         else:  | ||||
|             ks = sample(graph.vertices(), num)  | ||||
|             vs = [grid[k] for k in sample(ks, num)] | ||||
|             for k, v in zip(ks, vs): | ||||
|                 new_grid[k] = v           | ||||
|         n = edge_distances(graph, new_grid) | ||||
|         if m > n: | ||||
|             t = "\n{:.2%} at: {} tries of {}v shuffle/swap"  | ||||
|             print(t.format((n - m) / m, fail + 1, num), end="") | ||||
|             return new_grid | ||||
|         else: | ||||
|             fail += 1 | ||||
|     print(".", end='') | ||||
|     return grid | ||||
										
											Plik binarny nie jest wyświetlany.
										
									
								
							| Po Szerokość: | Wysokość: | Rozmiar: 391 KiB | 
|  | @ -0,0 +1,128 @@ | |||
| from __future__ import print_function, division | ||||
| from random import choice | ||||
| from graph import Graph | ||||
| from grid import setup_grid, grid_swap, edge_distances | ||||
| 
 | ||||
| thread_count = 0 | ||||
| gx, gy = 0, 100000 | ||||
| 
 | ||||
| def setup(): | ||||
|     size(400, 500) | ||||
|     fill(0) | ||||
|     textAlign(CENTER, CENTER) | ||||
|     f = createFont("Source Code Pro Bold", 14) | ||||
|     textFont(f) | ||||
|     setup_graph() | ||||
|      | ||||
| def setup_graph():     | ||||
|     global graph, grid | ||||
|     graph = Graph.random_graph(range(36), allow_loops=False) | ||||
|     grid = setup_grid(graph, width=width, height=width, margin=10) | ||||
|     global sel_v | ||||
|     sel_v = graph.get_random_vertex() | ||||
|     global path_walker, t_walker | ||||
|     path_walker = [] | ||||
|     t_walker = 0 | ||||
|     print(graph) | ||||
| 
 | ||||
| def draw(): | ||||
|     noStroke() | ||||
|     fill(150) | ||||
|     rect(0, 0, width, width) | ||||
|     noFill() | ||||
|     for e in graph.edges(): | ||||
|         va = e.pop() | ||||
|         xa, ya, za = grid[va] | ||||
|         if len(e) == 1: | ||||
|             vb = e.pop() | ||||
|             xb, yb, zb = grid[vb] | ||||
|             stroke(150) | ||||
|             strokeWeight(6) | ||||
|             line(xa, ya, xb, yb) | ||||
|             stroke(255) | ||||
|             strokeWeight(3) | ||||
|             line(xa, ya, xb, yb) | ||||
|         else: | ||||
|             circle(20 + xa, ya, 30) | ||||
|   | ||||
|     for v in grid.keys(): | ||||
|         x, y, z = grid[v] | ||||
|         fill(255) | ||||
|         circle(x, y, 10) | ||||
|         if keyPressed: | ||||
|             fill(0) | ||||
|             text("{}".format(v).upper(), x - 15, y - 3) | ||||
| 
 | ||||
|     walker()    | ||||
|     # graph_edge_distances() | ||||
|     stroke(0) | ||||
|     strokeWeight(1) | ||||
|     line(gx, height, gx, height - gy / 1000) | ||||
| 
 | ||||
| def walker():         | ||||
|     global t_walker, path_walker, sel_v | ||||
|     if path_walker and t_walker < 1: | ||||
|         # print(t_walker, path_walker) | ||||
|         p = lerpVectors(t_walker, path_walker) | ||||
|         noFill() | ||||
|         stroke(0, 0, 255) | ||||
|         circle(p.x, p.y, 10) | ||||
|         t_walker += .03 / len(path_walker) | ||||
|     else: | ||||
|         path_walker = [] | ||||
|         noStroke() | ||||
|         fill(255, 0, 0) | ||||
|         x, y, _ = grid[sel_v] | ||||
|         circle(x, y, 10)    | ||||
| 
 | ||||
| 
 | ||||
| def lerpVectors(amt, vecs): | ||||
|     """ from Jeremy Douglass """ | ||||
|     amt = constrain(amt, 0, 1)  # let's play safe | ||||
|     if len(vecs) == 1: | ||||
|         return vecs[0] | ||||
|     cunit = 1.0 / (len(vecs) - 1) | ||||
|     return PVector.lerp(vecs[floor(amt / cunit)], | ||||
|                         vecs[ceil(amt / cunit)], | ||||
|                         amt % cunit / cunit)         | ||||
| 
 | ||||
| def keyTyped(): | ||||
|     global gx, gy | ||||
|     if key == 'r': | ||||
|         setup_graph() | ||||
|         background(200) | ||||
|         gx, gy = 0, 100000 | ||||
|     else: | ||||
|         thread("swapping") | ||||
|          | ||||
| def swapping(): | ||||
|     global grid, thread_count, gx, gy | ||||
|     thread_count += 1 | ||||
|     this_thread, this_key = thread_count, str(key) | ||||
|     m = edge_distances(graph, grid) | ||||
|     print("\nStarting thread:{} key:{}".format(this_thread, key), end="") | ||||
|     len_graph = len(graph) | ||||
|     for _ in range(len_graph): | ||||
|         if this_key == 's': | ||||
|             grid = grid_swap(graph, grid, num = len_graph) | ||||
|         if this_key in '234556789': | ||||
|             grid = grid_swap(graph, grid, num=int(this_key)) | ||||
|         n = edge_distances(graph, grid) | ||||
|         gx += 1 | ||||
|         if n < m: | ||||
|             gy -= gy * (m - n) / m | ||||
|             m = n | ||||
|              | ||||
|     print("\nEnding thread :{}".format(this_thread), end="") | ||||
|                              | ||||
|                                                  | ||||
| def mousePressed(): | ||||
|     global path_walker, t_walker, sel_v | ||||
|     for v in graph.vertices(): | ||||
|         x, y, _ = grid[v] | ||||
|         if v != sel_v and dist(x, y, mouseX, mouseY) < 10: | ||||
|             path = graph.find_shortest_path(sel_v, v) | ||||
|             if path: | ||||
|                 path_walker = [PVector(*grid[pv]) for pv in path] | ||||
|                 t_walker = 0 | ||||
|                 sel_v = v  | ||||
|  | @ -23,8 +23,12 @@ | |||
| --- | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| [sketch_2020_08_09a](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_08_09a) [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)] | ||||
| [sketch_2020_08_09a](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_08_09a) |  | ||||
| [sketch_2020_08_09b](https://github.com/villares/sketch-a-day/tree/master/2020/sketch_2020_08_09b) | ||||
|   | ||||
| [[Py.Processing](https://villares.github.io/como-instalar-o-processing-modo-python/index-EN)] | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Alexandre B A Villares
						Alexandre B A Villares