diff --git a/.vs/Graphs/v16/.suo b/.vs/Graphs/v16/.suo new file mode 100644 index 000000000..981367cfe Binary files /dev/null and b/.vs/Graphs/v16/.suo differ diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 000000000..0d2272119 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,10 @@ +{ + "ExpandedNodes": [ + "", + "\\projects", + "\\projects\\ancestor", + "\\projects\\graph" + ], + "SelectedNode": "\\projects\\ancestor\\ancestor.py", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 000000000..a6328f59b Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/projects/ancestor/ancestor.py b/projects/ancestor/ancestor.py index 3bd003098..52f04dff5 100644 --- a/projects/ancestor/ancestor.py +++ b/projects/ancestor/ancestor.py @@ -1,3 +1,48 @@ +class Queue(): + def __init__(self): + self.queue = [] + def enqueue(self, value): + self.queue.append(value) + def dequeue(self): + if self.size() > 0: + return self.queue.pop(0) + else: + return None + def size(self): + return len(self.queue) +class Graph(): + def __init__(self): + self.verts = {} + def add_vertex(self, vertex_id): + if vertex_id not in self.verts: + self.verts[vertex_id] = set() + def add_edge(self, v1, v2): + if v1 in self.verts and v2 in self.verts: + self.verts[v1].add(v2) + def earliest_ancestor(ancestors, starting_node): - pass \ No newline at end of file + + g = Graph() + + for i in ancestors: + g.add_vertex(i[0]) + g.add_vertex(i[1]) + + g.add_edge(i[1], i[0]) + + q = Queue() + q.enqueue([starting_node]) + longest_path = 1 + earliest = -1 + while q.size() > 0: + path = q.dequeue() + v = path[-1] + if(len(path) >= longest_path and v < earliest) or (len(path) > longest_path): + earliest = v + longest_path = len(path) + for next_item in g.verts[v]: + copy = list(path) + copy.append(next_item) + q.enqueue(copy) + return earliest \ No newline at end of file diff --git a/projects/graph/graph.py b/projects/graph/graph.py index 59fecae4b..ad4d3ceab 100644 --- a/projects/graph/graph.py +++ b/projects/graph/graph.py @@ -1,80 +1,118 @@ """ Simple graph implementation """ -from util import Stack, Queue # These may come in handy +from collections import deque + class Graph: - """Represent a graph as a dictionary of vertices mapping labels to edges.""" + def __init__(self): self.vertices = {} def add_vertex(self, vertex_id): - """ - Add a vertex to the graph. - """ - pass # TODO + + if vertex_id not in self.vertices: + self.vertices[vertex_id] = set() def add_edge(self, v1, v2): - """ - Add a directed edge to the graph. - """ - pass # TODO + if v1 in self.vertices and v2 in self.vertices: + self.vertices[v1].add(v2) def get_neighbors(self, vertex_id): - """ - Get all neighbors (edges) of a vertex. - """ - pass # TODO + + return self.vertices[vertex_id] if vertex_id in self.vertices else set() def bft(self, starting_vertex): - """ - Print each vertex in breadth-first order - beginning from starting_vertex. - """ - pass # TODO - def dft(self, starting_vertex): - """ - Print each vertex in depth-first order - beginning from starting_vertex. - """ - pass # TODO + visited = set() + queue = deque() + queue.append(starting_vertex) + while len(queue) > 0: + currNode = queue.popleft() + if currNode not in visited: + visited.add(currNode) + print(currNode) + for neighbor in self.get_neighbors(currNode): + queue.append(neighbor) - def dft_recursive(self, starting_vertex): - """ - Print each vertex in depth-first order - beginning from starting_vertex. + def dft(self, starting_vertex): - This should be done using recursion. - """ - pass # TODO + visited = set() + stack = deque() + stack.append(starting_vertex) + while len(stack) > 0: + currNode = stack.pop() + if currNode not in visited: + visited.add(currNode) + print(currNode) + for neighbor in self.get_neighbors(currNode): + stack.append(neighbor) + + def dft_recursive(self, starting_vertex, visited=set()): + + visited.add(starting_vertex) + print(starting_vertex) + for neighbor in self.get_neighbors(starting_vertex): + if neighbor not in visited: + self.dft_recursive(neighbor, visited) def bfs(self, starting_vertex, destination_vertex): - """ - Return a list containing the shortest path from - starting_vertex to destination_vertex in - breath-first order. - """ - pass # TODO + + queue = deque() + queue.append([starting_vertex]) + visited = set() + while len(queue) > 0: + currPath = queue.popleft() + currNode = currPath[-1] + if currNode == destination_vertex: + return currPath + if currNode not in visited: + visited.add(currNode) + for neighbor in self.get_neighbors(currNode): + newPath = list(currPath) + newPath.append(neighbor) + queue.append(newPath) def dfs(self, starting_vertex, destination_vertex): - """ - Return a list containing a path from - starting_vertex to destination_vertex in - depth-first order. - """ - pass # TODO + + stack = deque() + stack.append([starting_vertex]) + visited = set() + while len(stack) > 0: + currPath = stack.pop() + currNode = currPath[-1] + if currNode == destination_vertex: + return currPath + if currNode not in visited: + visited.add(currNode) + for neighbor in self.get_neighbors(currNode): + newPath = list(currPath) + newPath.append(neighbor) + stack.append(newPath) def dfs_recursive(self, starting_vertex, destination_vertex): - """ - Return a list containing a path from - starting_vertex to destination_vertex in - depth-first order. - This should be done using recursion. - """ - pass # TODO + visited = set() + + def dfs(path): + currPath = path[-1] + + if currPath not in visited: + visited.add(currPath) + if currPath == destination_vertex: + return path + for neighbor in self.get_neighbors(currPath): + newPath = path[:] + newPath.append(neighbor) + foundPath = dfs(newPath) + if foundPath: + return foundPath + + return None + + return dfs([starting_vertex]) + if __name__ == '__main__': graph = Graph() # Instantiate your graph @@ -142,4 +180,4 @@ def dfs_recursive(self, starting_vertex, destination_vertex): [1, 2, 4, 7, 6] ''' print(graph.dfs(1, 6)) - print(graph.dfs_recursive(1, 6)) + print(graph.dfs_recursive(1, 6)) \ No newline at end of file diff --git a/projects/social/social.py b/projects/social/social.py index 8609d8800..94e5b5084 100644 --- a/projects/social/social.py +++ b/projects/social/social.py @@ -1,70 +1,106 @@ +import random + class User: def __init__(self, name): self.name = name + + def __repr__(self): + return f'User({repr(self.name)})' class SocialGraph: def __init__(self): self.last_id = 0 self.users = {} self.friendships = {} + + def reset(self): + self.last_id = 0 + self.users = {} + self.friendships = {} def add_friendship(self, user_id, friend_id): - """ - Creates a bi-directional friendship - """ + if user_id == friend_id: - print("WARNING: You cannot be friends with yourself") + + return False elif friend_id in self.friendships[user_id] or user_id in self.friendships[friend_id]: - print("WARNING: Friendship already exists") + + return False else: self.friendships[user_id].add(friend_id) self.friendships[friend_id].add(user_id) + + return True def add_user(self, name): - """ - Create a new user with a sequential integer ID - """ - self.last_id += 1 # automatically increment the ID to assign the new user + + self.last_id += 1 self.users[self.last_id] = User(name) self.friendships[self.last_id] = set() def populate_graph(self, num_users, avg_friendships): - """ - Takes a number of users and an average number of friendships - as arguments - Creates that number of users and a randomly distributed friendships - between those users. + self.reset() - The number of users must be greater than the average number of friendships. - """ - # Reset graph - self.last_id = 0 - self.users = {} - self.friendships = {} - # !!!! IMPLEMENT ME + for i in range(num_users): + self.add_user(f"User {i}") - # Add users + possible_friendships = [] + + for user_id in self.users: + for friend_id in range(user_id + 1, self.last_id + 1): + possible_friendships.append((user_id, friend_id)) + + random.shuffle(possible_friendships) + + for i in range(num_users * avg_friendships // 2): + friendships = possible_friendships[i] + self.add_friendship(friendships[0], friendships[1]) + + def populate_graph_2(self, num_users, avg_friendships): - # Create friendships + self.reset() - def get_all_social_paths(self, user_id): - """ - Takes a user's user_id as an argument + for i in range(num_users): + self.add_user(f"User {i}") + + target_friendships = num_users * avg_friendships + total_friendships = 0 + + collisions = 0 + + while total_friendships < target_friendships: + user_id = random.randint(1, self.last_id) + friend_id = random.randint(1, self.last_id) + + if self.add_friendship(user_id, friend_id): + total_friendships += 2 + else: + collisions += 1 + + print(f"COLLISIONS: {collisions}") - Returns a dictionary containing every user in that user's - extended network with the shortest friendship path between them. + def get_all_social_paths(self, user_id): - The key is the friend's ID and the value is the path. - """ - visited = {} # Note that this is a dictionary, not a set - # !!!! IMPLEMENT ME + visited = {} + q = Queue() + q.enqueue([user_id]) + while q.size() > 0: + path = q.dequeue() + newuser_id = path[-1] + if newuser_id not in visited: + visited[newuser_id] = path + for friend_id in self.friendships[newuser_id]: + if friend_id not in visited: + new_path = list(path) + new_path.append(friend_id) + q.enqueue(new_path) return visited - if __name__ == '__main__': - sg = SocialGraph() - sg.populate_graph(10, 2) - print(sg.friendships) - connections = sg.get_all_social_paths(1) - print(connections) + sogh = SocialGraph() + sogh.populate_graph(1000, 999) + print(sogh.users) + print(sogh.friendships) + connections = sogh.get_all_social_paths(1) + print(connections) \ No newline at end of file