import random import time import matplotlib.pyplot as plt import sys from typing import List def generateGraph(n: int, saturation: int): edges = (n * (n - 1)) // 2 maxEdges = edges * saturation // 100 graph = {i: [] for i in range(n)} edgeCount = 0 # Generate a Hamiltonian cycle cycle = list(range(n)) random.shuffle(cycle) for i in range(n): u = cycle[i] v = cycle[(i + 1) % n] graph[u].append(v) graph[v].append(u) edgeCount += 1 while edgeCount < maxEdges: # Find all pairs (u, v) where u < v, u != v, v not in graph[u], and deg(u)%2 == deg(v)%2 candidates = [] for u in range(n): for v in range(u + 1, n): if v not in graph[u] and (len(graph[u]) % 2) == (len(graph[v]) % 2): candidates.append((u, v)) if not candidates: break # No more valid pairs to add without breaking Eulerian property u, v = random.choice(candidates) graph[u].append(v) graph[v].append(u) edgeCount += 1 for i in range(n): random.shuffle(graph[i]) print(f"Generated graph with {n} nodes and {edgeCount} edges (saturation: {saturation}%, max edges: {maxEdges})") # # Print the graph as a matrix for debugging # graph_matrix = [[0] * n for _ in range(n)] # for u in range(n): # for v in graph[u]: # graph_matrix[u][v] = 1 # print("Graph adjacency matrix:") # for row in graph_matrix: # print(" ".join(map(str, row))) return graph def graphFromMatrix(matrix: List[List[int]]): n = len(matrix) graph = {i: [] for i in range(n)} for i in range(n): for j in range(n): if matrix[i][j] == 1: graph[i].append(j) return graph def findEulerianCycle(graph_adj_sets) -> List[int] | None: current_graph = {v: set(neighbors) for v, neighbors in graph_adj_sets.items()} if not current_graph: return None # No nodes, empty path path = [] start_vertex = -1 for v_check, neigh_check in current_graph.items(): if neigh_check: start_vertex = v_check break if start_vertex == -1: if len(current_graph) == 1: return [next(iter(current_graph))] # Single node graph return None stack = [start_vertex] while stack: v = stack[-1] if current_graph.get(v): u = current_graph[v].pop() current_graph[u].remove(v) stack.append(u) # Move to the next vertex else: path.append(stack.pop()) # Backtrack and add to the path return path[::-1] # Return the path in reverse order def findHamiltonianCycle(graph): n = len(graph) path = [] def backtrack(v, visited): if len(path) == n: return path[0] in graph[v] # Check if the last node connects to the first for neighbor in graph[v]: if neighbor not in visited: visited.add(neighbor) path.append(neighbor) if backtrack(neighbor, visited): return True visited.remove(neighbor) path.pop() return False for start in range(n): path.append(start) visited = {start} if backtrack(start, visited): return path path.pop() return None if __name__ == "__main__": # Check if there was given a second argument, if yes run a test instead of the benchmark if len(sys.argv) > 1: file = sys.argv[1] """ Format we are expecting: 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 0 1 1 1 0 1 1 0 1 1 0 1 1 1 0 """ with open(file, 'r') as f: lines = f.readlines() matrix = [list(map(int, line.strip().split())) for line in lines] graph = graphFromMatrix(matrix) euler = findEulerianCycle(graph) hamilton = findHamiltonianCycle(graph) if euler is None: print("Eulerian cycle not found.") exit(1) if hamilton is None: print("Hamiltonian cycle not found.") exit(1) # We were asked to print the cycles starting from 1. Add the first node to the end of the list to make it a cycle # Expected: 1 6 5 4 6 3 5 1 4 2 3 1 | Actual: 1 3 2 4 1 5 3 6 4 5 6 1 print("Eulerian Cycle:", [v + 1 for v in euler ]) # 1 3 2 4 5 6 1 | Real: 1 3 2 4 5 6 1 print("Hamiltonian Cycle", [v + 1 for v in hamilton] + [hamilton[0] + 1]) exit(0) n_values = range(10, 26) # Number of nodes to test saturations = [30, 70] # Saturation levels to test results = {} for saturation in saturations: hamilton_times = [] eulerian_times = [] for n in n_values: print(f"Running tests for {n} nodes with {saturation}% saturation...") graph = generateGraph(n, saturation) eulerian_time = 0 hamilton_time = 0 for _ in range(10): start_time = time.time() if findHamiltonianCycle(graph) is None: raise ValueError("Hamiltonian cycle not found, which should not happen with the generated graph.") end_time = time.time() measured_time = (end_time - start_time) * 1000 hamilton_time += measured_time eulerian_graph_repr = {v_node: set(v_neighbors) for v_node, v_neighbors in graph.items()} start_time = time.time() graph_for_eulerian_run = {node: set(adj_nodes) for node, adj_nodes in graph.items()} start_time = time.time() if findEulerianCycle(graph_for_eulerian_run) is None: # Pass the new set-based representation raise ValueError("Eulerian cycle not found...") end_time = time.time() eulerian_time += (end_time - start_time) * 1000 # Average the times over 10 runs hamilton_times.append(hamilton_time / 10) eulerian_times.append(eulerian_time / 10) time.sleep(0.1) # Sleep to avoid overwhelming the system results[saturation] = { "hamilton_times": hamilton_times, "eulerian_times": eulerian_times, } plt.figure(figsize=(10, 6)) plt.plot(n_values, hamilton_times, label="Cykl Hamiltona", marker="o") plt.plot(n_values, eulerian_times, label="Cykl Eulera", marker="s") plt.xlabel("Liczba wierzchołków (n)") plt.ylabel("Czas działania (ms)") plt.title(f"Czas działania algorytmów dla {saturation}% nasycenia") plt.legend() plt.grid(True) if(saturation == 30): plt.yscale('log', base=2) filename = f"saturation_{saturation}.png" plt.savefig(filename) print(f"Plot saved as '{filename}'") print("All tests completed.")