diff --git a/Lab2/.gitignore b/.gitignore similarity index 75% rename from Lab2/.gitignore rename to .gitignore index c3226e8..8f869ef 100644 --- a/Lab2/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ benchmark *.csv charts/ +direnv/ diff --git a/Lab3/graph.py b/Lab3/graph.py new file mode 100644 index 0000000..ae578fc --- /dev/null +++ b/Lab3/graph.py @@ -0,0 +1,173 @@ +import random +import time +import matplotlib.pyplot as plt + +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 findEulerianCycle(graph_adj_sets): + 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__": + 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.") diff --git a/flake.nix b/flake.nix index a3a536f..f0b2646 100644 --- a/flake.nix +++ b/flake.nix @@ -20,14 +20,6 @@ clang-tools cmake libgcc - codespell - conan - cppcheck - doxygen - gtest - lcov - vcpkg - vcpkg-tool python312Packages.matplotlib ] ++ (if system == "aarch64-darwin" then [ ] else [ gdb ]); };