diff --git a/lecture.py b/lecture.py new file mode 100644 index 000000000..e8db3391f --- /dev/null +++ b/lecture.py @@ -0,0 +1,36 @@ +class Queue(object): + """ + docstring + """ + + 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 Stack(): + def __init__(self): + self.stack = [] + + def push(self, value): + self.stack.append(value) + + def pop(self): + if self.size() > 0: + return self.stack.pop() + else: + return None + + def size(self): + return len(self.stack) diff --git a/lecture3.py b/lecture3.py new file mode 100644 index 000000000..f63b55b21 --- /dev/null +++ b/lecture3.py @@ -0,0 +1,82 @@ +islands = [ + [0, 1, 0, 1, 0], + [1, 1, 0, 1, 1], + [0, 0, 1, 0, 0], + [1, 0, 1, 0, 0], + [1, 1, 0, 0, 0] +] + + +def island_counter(islands): + # create a way to keep track of visited nodes + visited = [] + for _ in range(len(islands)): + new_row = [False] * len(islands[0]) + visited.append(new_row) + + # print(visited) + + island_count = 0 + # walk through each + for row in range(len(islands)): + for col in range(len(islands[0])): + # if it is not visited + if not visited[row][col]: + # if its a 1: + if islands[row][col] == 1: + # do a traversal + dft(row, col, islands, visited) + # increment the counter + island_count += 1 + return island_count + + +def dft(row, col, islands, visited): + s = [] + + s.append((row, col)) + + while len(s) > 0: + v = s.pop() + row, col = v + if not visited[row][col]: + visited[row][col] = True + + for neighbor in get_neighbors(row, col, islands): + s.append(neighbor) + + # what are the neighbors of (1,3) + + +def get_neighbors(row, col, islands): + neighbors = [] + # check north + if row > 0 and islands[row-1][col] == 1: + neighbors.append((row-1, col)) + # check south + if row < len(islands) - 1 and islands[row+1][col] == 1: + neighbors.append((row+1, col)) + # check west + if col > 0 and islands[row][col-1] == 1: + neighbors.append((row, col-1)) + # check east + if col < len(islands) - 1 and islands[row][col+1] == 1: + neighbors.append((row, col+1)) + + return neighbors + + +print(island_counter(islands)) # returns 4 + + +""" +When is DFS better? + - might find the longest path + - if you suspect the target is deep within the graph + - if the target node is a leaf + - can be implemented recursively, or randomly + +When is BFS better? + - might find the shortest path + +""" diff --git a/projects/adventure/adv.py b/projects/adventure/adv.py index 8bc540b5e..b4be0c48c 100644 --- a/projects/adventure/adv.py +++ b/projects/adventure/adv.py @@ -17,19 +17,200 @@ map_file = "maps/main_maze.txt" # Loads the map into a dictionary -room_graph=literal_eval(open(map_file, "r").read()) +room_graph = literal_eval(open(map_file, "r").read()) world.load_graph(room_graph) -# Print an ASCII map +# Print an ASCII map≄ world.print_rooms() player = Player(world.starting_room) # Fill this out with directions to walk # traversal_path = ['n', 'n'] -traversal_path = [] +def bfs(starting_vertex): + q = [] + visit = [] + path = [starting_vertex] + travel = [] + q.append(path) + # is the node in graph + # while queue isn't empty + while len(q) > 0: + # dequeue the node at the front of the line + current_path = q.pop(0) + # print("current path", current_path) + current_node = current_path[-1] + # print(current_node, "starting node") + # print("current_node bfs", current_node) + # if this node is a target node, return true + if has_exits(current_node): + # print("current_path return", current_path) + # traversal_path.extend(travel) + return current_path + + # return current_path + if current_node not in visit: + visit.append(current_node) + # neighbors = + neighbors = [i for i in graph[current_node]] + # print("neighbors", neighbors) + for i in neighbors: + # if graph[current_node][i] < current_node: + # print("available", current_path + [graph[current_node][i]]) + q.append(current_path + [graph[current_node][i]]) + # else: + # continue + # else: + # print("BAHAHA", has_exits(current_node)) + + +def has_exits(vertex): + free = [] + for key, value in graph[vertex].items(): + # print(key, value, "vertex", vertex) + if value == "?": + free.append(key) + if len(free) > 0: + # print("free", free) + return free + return False + + +traversal_path = [] +graph = {} +current = player.current_room.id +visited = [] +nodes = [] +last = '' + +s = [] +s.append(current) +print(current, "current") +print(len(world.rooms)) + +while len(visited) != len(world.rooms): + vertex = s.pop() + nodes.append(vertex) + # print("vertex", vertex) + exits = player.current_room.get_exits() + index = random.randint(0, len(exits)-1) + random_dir = exits[index] + free_exits = [] + + if vertex not in visited: + graph[vertex] = {} + visited.append(vertex) + # print("visited", visited) + for i in exits: + graph[vertex][i] = "?" + if len(traversal_path) > 0: + last = traversal_path[-1] + # print("traversal path", traversal_path) + # print("Nodes", nodes) + if last == "n": + if graph[vertex]['s'] == "?": + graph[vertex]['s'] = nodes[-2] + elif last == "s": + if graph[vertex]['n'] == "?": + graph[vertex]['n'] = nodes[-2] + elif last == "w": + if graph[vertex]['e'] == "?": + graph[vertex]['e'] = nodes[-2] + elif last == "e": + if graph[vertex]['w'] == "?": + graph[vertex]['w'] = nodes[-2] + for i in exits: + if graph[vertex][i] == "?": + free_exits.append(i) + # print("free exits", free_exits) + if len(free_exits) > 1: + index = random.randint(0, len(free_exits)-1) + random_dir = free_exits[index] + player.travel(random_dir) + traversal_path.append(random_dir) + graph[vertex][random_dir] = player.current_room.id + s.append(player.current_room.id) + elif len(free_exits) == 1: + player.travel(free_exits[0]) + traversal_path.append(free_exits[0]) + graph[vertex][free_exits[0]] = player.current_room.id + s.append(player.current_room.id) + else: + if len(visited) == len(world.rooms): + break + + new_vertex = bfs(player.current_room.id) + # print(graph) + for i in range(len(new_vertex)-1): + # print(new_vertex[i], "i top") + for key, value in graph[new_vertex[i]].items(): + # print('x top', key, value) + if value == new_vertex[i+1]: + # print(traversal_path, value, new_vertex[i+1], "before") + player.travel(key) + traversal_path.append(key) + # print(traversal_path, "after") + # print("new vertex", new_vertex) + # print("breaking new vertex top", new_vertex) + # print(len(visited)) + # print("traversal length", len(traversal_path)) + s.append(new_vertex[-1]) + else: + if len(traversal_path) > 0: + last = traversal_path[-1] + if last == "n": + if graph[vertex]['s'] == "?": + graph[vertex]['s'] = nodes[-2] + elif last == "s": + if graph[vertex]['n'] == "?": + graph[vertex]['n'] = nodes[-2] + elif last == "w": + if graph[vertex]['e'] == "?": + graph[vertex]['e'] = nodes[-2] + elif last == "e": + if graph[vertex]['w'] == "?": + graph[vertex]['w'] = nodes[-2] + for i in exits: + if graph[vertex][i] == "?": + free_exits.append(i) + if len(free_exits) > 1: + index = random.randint(0, len(free_exits)-1) + random_dir = free_exits[index] + player.travel(random_dir) + traversal_path.append(random_dir) + graph[vertex][random_dir] = player.current_room.id + s.append(player.current_room.id) + elif len(free_exits) == 1: + player.travel(free_exits[0]) + traversal_path.append(free_exits[0]) + graph[vertex][free_exits[0]] = player.current_room.id + s.append(player.current_room.id) + else: + # find an open exit vertex and travel there. + if len(visited) == len(world.rooms): + break + new_vertex = bfs(player.current_room.id) + for i in range(len(new_vertex)-1): + # print(new_vertex[i], "i top") + for key, value in graph[new_vertex[i]].items(): + # print('x top', key, value) + if value == new_vertex[i+1]: + # print(traversal_path, "before") + player.travel(key) + traversal_path.append(key) + # print(traversal_path, "after") + # print("breaking new vertex bottom", new_vertex) + # print(graph) + # print(len(visited)) + s.append(new_vertex[-1]) + + +# print("final graph", graph) +print("final len visited", len(visited)) +print("final len rooms", len(world.rooms)) +# print(has_exits(graph[0]) # TRAVERSAL TEST visited_rooms = set() @@ -41,22 +222,22 @@ visited_rooms.add(player.current_room) if len(visited_rooms) == len(room_graph): - print(f"TESTS PASSED: {len(traversal_path)} moves, {len(visited_rooms)} rooms visited") + print( + f"TESTS PASSED: {len(traversal_path)} moves, {len(visited_rooms)} rooms visited") else: print("TESTS FAILED: INCOMPLETE TRAVERSAL") print(f"{len(room_graph) - len(visited_rooms)} unvisited rooms") - ####### # UNCOMMENT TO WALK AROUND ####### -player.current_room.print_room_description(player) -while True: - cmds = input("-> ").lower().split(" ") - if cmds[0] in ["n", "s", "e", "w"]: - player.travel(cmds[0], True) - elif cmds[0] == "q": - break - else: - print("I did not understand that command.") +# player.current_room.print_room_description(player) +# while True: +# cmds = input("-> ").lower().split(" ") +# if cmds[0] in ["n", "s", "e", "w"]: +# player.travel(cmds[0], True) +# elif cmds[0] == "q": +# break +# else: +# print("I did not understand that command.") diff --git a/projects/adventure/fresh.py b/projects/adventure/fresh.py new file mode 100644 index 000000000..e69de29bb diff --git a/projects/ancestor/ancestor.py b/projects/ancestor/ancestor.py index 3bd003098..1351c160a 100644 --- a/projects/ancestor/ancestor.py +++ b/projects/ancestor/ancestor.py @@ -1,3 +1,54 @@ + def earliest_ancestor(ancestors, starting_node): - pass \ No newline at end of file + # set up our graph + graphy = {} + for tup in ancestors: + if tup[1] not in graphy: + graphy[tup[1]] = set() + graphy[tup[1]].add(tup[0]) + + print(graphy) + + path = [starting_node] + q = [path] + result = [] + + if get_parents(starting_node, graphy) == []: + return -1 + + while len(q) > 0: + new_path = q.pop(0) + vertex = new_path[-1] + parents = get_parents(vertex, graphy) + print("parents, vertex", parents, vertex) + + if parents == []: + result.append(new_path) + else: + for i in parents: + pathy = new_path + [i] + q.append(pathy) + + size = len(result[0]) + for i in result: + if len(i) > size: + size = len(i) + new_results = list(filter(lambda x: len(x) >= size, result)) + + # print("new results", new_results) + return min([i[-1] for i in new_results]) + + +def get_parents(node, graph): + if node in graph: + parents = list(graph[node]) + else: + parents = [] + return parents + + +ancestors = [(1, 3), (2, 3), (3, 6), (5, 6), (5, 7), + (4, 5), (4, 8), (8, 9), (11, 8), (10, 1)] + +print(earliest_ancestor(ancestors, 6)) diff --git a/projects/graph/graph.py b/projects/graph/graph.py index 59fecae4b..1be414e8c 100644 --- a/projects/graph/graph.py +++ b/projects/graph/graph.py @@ -3,9 +3,11 @@ """ from util import Stack, Queue # These may come in handy + class Graph: """Represent a graph as a dictionary of vertices mapping labels to edges.""" + def __init__(self): self.vertices = {} @@ -13,26 +15,45 @@ def add_vertex(self, vertex_id): """ Add a vertex to the graph. """ - pass # TODO + self.vertices[vertex_id] = set() def add_edge(self, v1, v2): """ Add a directed edge to the graph. """ - pass # TODO + 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] def bft(self, starting_vertex): """ Print each vertex in breadth-first order beginning from starting_vertex. """ - pass # TODO + # make a queue + q = Queue() + # enqueue our starting node + q.enqueue(starting_vertex) + # make a set to track if we have been here before + visited = set() + # while queue isn't empty, + while q.size() > 0: + # dequeue whatever is at the front of our line, this is our current node + current_node = q.dequeue() + # if we haven't visited this node yet, + if current_node not in visited: + # mark as visited + print(current_node) + visited.add(current_node) + # get its neighbors + neighbors = self.get_neighbors(current_node) + # foreach neighbor enqueue + for neighbor in neighbors: + q.enqueue(neighbor) def dft(self, starting_vertex): """ @@ -40,15 +61,58 @@ def dft(self, starting_vertex): beginning from starting_vertex. """ pass # TODO + # make a stack + s = Stack() + # push our starting node + s.push(starting_vertex) + # make a set to track if we've been here before + visited = set() + # while our stack isnt empty + while s.size() > 0: + # pop off whatevers on top, this is current node + current = s.pop() + # if we haven't visited this vertex before + if current not in visited: + # mark as visited + print(current) + visited.add(current) + # get its neighbors + neighbors = self.get_neighbors(current) + # for each of the neighbors + for i in neighbors: + # add to our stack + s.push(i) - def dft_recursive(self, starting_vertex): + def dft_recursive(self, vertex, visited=set()): """ Print each vertex in depth-first order beginning from starting_vertex. This should be done using recursion. """ - pass # TODO + # # mark this vertex as visited + # visited.add(starting_vertex) + # print(starting_vertex) + # # for each neighbor + # neighbors = self.get_neighbors(starting_vertex) + # for i in neighbors: + # # if it is not visited + # if i not in visited: + # # recurse on teh neighbor + # self.dft_recursive(i, visited) + + if vertex not in visited: + print(vertex) + + visited.add(vertex) + + neighbors = self.get_neighbors(vertex) + if len(neighbors) == 0: + return + + else: + for neighbor in neighbors: + self.dft_recursive(neighbor, visited) def bfs(self, starting_vertex, destination_vertex): """ @@ -56,7 +120,30 @@ def bfs(self, starting_vertex, destination_vertex): starting_vertex to destination_vertex in breath-first order. """ - pass # TODO + q = Queue() + visited = set() + path = [starting_vertex] + + q.enqueue(path) + # is the node in graph + # while queue isn't empty + while q.size() > 0: + # dequeue the node at the front of the line + current_path = q.dequeue() + current_node = current_path[-1] + # if this node is a target node, return true + if current_node == destination_vertex: + return current_path + if current_node not in visited: + visited.add(current_node) + neighbors = self.get_neighbors(current_node) + for i in neighbors: + # copy path so we don't mutate original + q.enqueue(current_path + [i]) + # if not visited, mark as visited + # get its neighbors + # for each neighbor + # add to our queue def dfs(self, starting_vertex, destination_vertex): """ @@ -64,9 +151,25 @@ def dfs(self, starting_vertex, destination_vertex): starting_vertex to destination_vertex in depth-first order. """ - pass # TODO + s = Stack() + visited = set() + s.push([starting_vertex]) + + while s.size() > 0: + path = s.pop() + final = path[-1] + if final not in visited: + if final == destination_vertex: + return path + # print(final) + visited.add(final) + + for i in self.get_neighbors(final): + pathy = list(path) + pathy.append(i) + s.push(pathy) - def dfs_recursive(self, starting_vertex, destination_vertex): + def dfs_recursive(self, starting_vertex, destination_vertex, path=[], visited=set()): """ Return a list containing a path from starting_vertex to destination_vertex in @@ -74,7 +177,42 @@ def dfs_recursive(self, starting_vertex, destination_vertex): This should be done using recursion. """ - pass # TODO + # visited.add(starting_vertex) + # if starting_vertex == destination_vertex: + # return path + + # if len(path) == 0: + # path.append(starting_vertex) + + # neighbors = self.get_neighbors(starting_vertex) + + # for neighbor in neighbors: + # if neighbor not in visited: + # result = self.dfs_recursive( + # neighbor, destination_vertex, path + [neighbor], visited) + # if result is not None: + # return result + if len(path) == 0: + path.append(starting_vertex) + if starting_vertex == destination_vertex: + return path + + if starting_vertex not in visited: + visited.add(starting_vertex) + + neighbors = self.get_neighbors(starting_vertex) + + for neighbor in neighbors: + new_path = path + [neighbor] + + # only return if we have found the destination vertex + result = self.dfs_recursive( + neighbor, destination_vertex, new_path, visited) + if result is not None: + return result + else: + return None + if __name__ == '__main__': graph = Graph() # Instantiate your graph @@ -101,7 +239,6 @@ def dfs_recursive(self, starting_vertex, destination_vertex): Should print: {1: {2}, 2: {3, 4}, 3: {5}, 4: {6, 7}, 5: {3}, 6: {3}, 7: {1, 6}} ''' - print(graph.vertices) ''' Valid BFT paths: @@ -141,5 +278,5 @@ def dfs_recursive(self, starting_vertex, destination_vertex): [1, 2, 4, 6] [1, 2, 4, 7, 6] ''' - print(graph.dfs(1, 6)) - print(graph.dfs_recursive(1, 6)) + # print(graph.dfs(1, 6)) + # print(graph.dfs_recursive(1, 6)) diff --git a/projects/graph/graph_practice.py b/projects/graph/graph_practice.py new file mode 100644 index 000000000..8827045f1 --- /dev/null +++ b/projects/graph/graph_practice.py @@ -0,0 +1,158 @@ +from util import Stack, Queue + + +class Graph: + def __init__(self): + self.vertices = {} + + def add_vertex(self, vertex): + self.vertices[vertex] = set() + + def add_edge(self, vertex1, vertex2): + self.vertices[vertex1].add(vertex2) + + def find_neighbors(self, vertex): + return self.vertices[vertex] + + def bft(self, start_vertex, visited=[]): + q = Queue() + + q.enqueue(start_vertex) + + while q.size() > 0: + vertex = q.dequeue() + if vertex not in visited: + visited.append(vertex) + print(vertex) + else: + break + for neighbor in self.find_neighbors(vertex): + q.enqueue(neighbor) + + def dft(self, start_vertex, visited=[]): + s = Stack() + s.push(start_vertex) + + while s.size() > 0: + vertex = s.pop() + if vertex not in visited: + visited.append(vertex) + print(vertex) + else: + break + for neighbor in self.find_neighbors(vertex): + s.push(neighbor) + + def bfs(self, start_vertex, target_vertex, visited=[]): + q = Queue() + + q.enqueue([start_vertex]) + + while q.size() > 0: + new_path = q.dequeue() + vertex = new_path[-1] + if vertex not in visited: + if vertex == target_vertex: + print(new_path) + return new_path + else: + visited.append(vertex) + + for neighbor in self.find_neighbors(vertex): + pathy = new_path + [neighbor] + q.enqueue(pathy) + + def dfs(self, start_vertex, target_vertex, visited=[]): + s = Stack() + + s.push([start_vertex]) + + while s.size() > 0: + new_path = s.pop() + vertex = new_path[-1] + if vertex not in visited: + if vertex == target_vertex: + print(new_path) + return new_path + else: + visited.append(vertex) + + for neighbor in self.find_neighbors(vertex): + pathy = new_path + [neighbor] + s.push(pathy) + + def dfs_recurse(self, start_vertex, target_vertex, visited=None, path=None): + if visited is None: + visited = set() + if path == None: + path = [] + visited.add(start_vertex) + + path = path + [start_vertex] + + if start_vertex == target_vertex: + return path + + for neighbor in self.find_neighbors(start_vertex): + if neighbor not in visited: + new_path = self.dfs_recurse( + neighbor, target_vertex, visited, path) + + if new_path is not None: + return new_path + + return None + + +graph = Graph() +graph.add_vertex(1) +graph.add_vertex(2) +graph.add_vertex(3) +graph.add_vertex(4) +graph.add_vertex(5) +graph.add_vertex(6) +graph.add_vertex(7) +graph.add_edge(5, 3) +graph.add_edge(6, 3) +graph.add_edge(7, 1) +graph.add_edge(4, 7) +graph.add_edge(1, 2) +graph.add_edge(7, 6) +graph.add_edge(2, 4) +graph.add_edge(3, 5) +graph.add_edge(2, 3) +graph.add_edge(4, 6) + +print(graph.vertices) +# graph.bft(1) +# graph.dft(1) + +# print(graph.bfs(1, 6)) +print(graph.dfs_recurse(1, 6)) + +# graph.dft(1) + +# g = Graph() +# g.add_vertex(5) +# g.add_vertex(10) +# g.add_vertex(8) +# g.add_vertex(3) +# g.add_vertex(4) +# g.add_vertex(7) +# g.add_vertex(6) +# g.add_vertex(9) + + +# g.add_edge(5, 8) +# g.add_edge(5, 10) +# g.add_edge(10, 6) +# g.add_edge(10, 3) +# g.add_edge(8, 3) +# g.add_edge(3, 7) +# g.add_edge(3, 4) +# g.add_edge(4, 9) + +# # g.bft(5) +# # g.dft(5) +# # g.bfs(5, 9) +# g.dfs(5, 9) diff --git a/projects/graph/lecture_example.py b/projects/graph/lecture_example.py new file mode 100644 index 000000000..48ca6acc0 --- /dev/null +++ b/projects/graph/lecture_example.py @@ -0,0 +1,81 @@ +# read words in from a file + +import string + +word_set = set() +with open("words.txt", "r") as f: + for word in f: + word_set.add(word.strip().lower()) + +words_by_length = {} +for w in word_set: + l = len(w) + + if l not in words_by_length: + words_by_length[l] = [] + + words_by_length[l].append(w) + + +def get_neighbors(word): + neighbors = [] + word_letters = list(word) + for i in range(len(word_letters)): + for letter in list(string.ascii_lowercase): + # make a copy or word + temp_word = list(word_letters) + # substitute the letter into the word copy + temp_word[i] = letter + # make it a string + temp_word_str = "".join(temp_word) + # if it is a real word, add it to the return set + if temp_word_str != word and temp_word_str in word_set: + neighbors.append(temp_word_str) + + +def get_neighbors2(word): + + def word_diff_by_1(w1, w2): + if len(w1) != len(w2): + return False + diff_count = 0 + + for i in range(len(w1)): + if w1[i] != w2[i]: + diff_count += 1 + + return diff_count == 1 + + neighbors = [] + + for word2 in words_by_length[len(word)]: + if word_diff_by_1(word, word2): + neighbors.append(word2) + return neighbors + # look for words of same length + + # if they differ by one letter, add to the return set + + +def find_word_ladder(begin_word, end_word): + visited = set() + q = Queue() + + q.enqueue([begin_word]) + + while q.size() > 0: + path = q.dequeue() + last_word = path[-1] + + if last_word not in visited: + visited.add(last_word) + + if last_word == end_word: + return path + + for neighbor in get_neighbors(last_word): + path_copy = path + [neighbor] + q.enqueue(path_copy) + + # If we get here and haven't found it: + return None diff --git a/projects/social/README.md b/projects/social/README.md index f13000457..2f811b0ca 100644 --- a/projects/social/README.md +++ b/projects/social/README.md @@ -47,14 +47,23 @@ Note that in this sample, Users 3, 4 and 9 are not in User 1's extended social n ## 3. Questions 1. To create 100 users with an average of 10 friends each, how many times would you need to call `add_friendship()`? Why? +"500. Not exactly sure why we split total friendships by 2. " -2. If you create 1000 users with an average of 5 random friends each, what percentage of other users will be in a particular user's extended social network? What is the average degree of separation between a user and those in his/her extended network? +total_friendships = 100 * 10 +2. If you create 1000 users with an average of 5 random friends each, what percentage of other users will be in a particular user's extended social network? What is the average degree of separation between a user and those in his/her extended network? +99% +5-6 ## 4. Stretch Goal 1. You might have found the results from question #2 above to be surprising. Would you expect results like this in real life? If not, what are some ways you could improve your friendship distribution model for more realistic results? +Yeah, there are like 6 degrees of separation. Number of friendships are not super random. There are probably clusters. + 2. If you followed the hints for part 1, your `populate_graph()` will run in O(n^2) time. Refactor your code to run in O(n) time. Are there any tradeoffs that come with this implementation? +#choose two random user_ids +#try to make the friendship +#do this until we have as many as we want diff --git a/projects/social/social.py b/projects/social/social.py index 8609d8800..8de40e5f3 100644 --- a/projects/social/social.py +++ b/projects/social/social.py @@ -1,12 +1,21 @@ + +import random + + class User: def __init__(self, name): self.name = name + import random + + class SocialGraph: def __init__(self): - self.last_id = 0 - self.users = {} + self.last_id = 0 # the number of users you currently have + self.users = {} # users with attribs, wont use self.friendships = {} + # {1: {8, 10, 5}, 2: {10, 5, 7}, 3: {4}, 4: {9, 3}, 5: { + # 8, 1, 2}, 6: {10}, 7: {2}, 8: {1, 5}, 9: {4}, 10: {1, 2, 6}} # adjacency list def add_friendship(self, user_id, friend_id): """ @@ -14,11 +23,16 @@ def add_friendship(self, user_id, friend_id): """ 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 get_friends(self, user_id): + return self.friendships[user_id] def add_user(self, name): """ @@ -28,6 +42,39 @@ def add_user(self, name): self.users[self.last_id] = User(name) self.friendships[self.last_id] = set() + def fisher_yates_shuffle(self, l): + for i in range(0, len(l)): + random_index = random.randint(i, len(l) - 1) + l[random_index], l[i] = l[i], l[random_index] + + def bft_path(self, start_vertex): + q = [] + visited = {} + path = [start_vertex] + q.append(path) + # result = {} + + while len(q) > 0: + # print("queue", q) + new_path = q.pop(0) + vertex = new_path[-1] + # print("vertex", vertex) + + if vertex not in visited: + visited[vertex] = new_path + # if vertex == start_vertex: + # result[start_vertex] = new_path + # if len(self.get_friends(vertex)) == 0: + # all_paths.append(new_path) + # else: + for neighbor in self.get_friends(vertex): + # if neighbor in new_path: + # result[vertex] = new_path + # else: + copy_path = new_path + [neighbor] + q.append(copy_path) + return visited + def populate_graph(self, num_users, avg_friendships): """ Takes a number of users and an average number of friendships @@ -45,26 +92,113 @@ def populate_graph(self, num_users, avg_friendships): # !!!! IMPLEMENT ME # Add users - + for user in range(num_users): + self.add_user(user) # Create friendships + total_friendships = avg_friendships * num_users + # create a list of all possible friendships + friendship_combos = [] + + for user_id in range(1, num_users + 1): + for friend_id in range(user_id+1, num_users+1): + friendship_combos.append((user_id, friend_id)) + + # shuffle the list, + self.fisher_yates_shuffle(friendship_combos) + # then grab first n elements from the list + # print("frinedshipt combos", friendship_combos) + friendships_to_make = friendship_combos[:(total_friendships // 2)] + # only create friendships where user1< user2 + for friendship in friendships_to_make: + self.add_friendship(friendship[0], friendship[1]) def get_all_social_paths(self, user_id): """ Takes a user's user_id as an argument Returns a dictionary containing every user in that user's - extended network with the shortest friendship path between them. The key is the friend's ID and the value is the path. + extended network with the shortest friendship path between them. """ visited = {} # Note that this is a dictionary, not a set # !!!! IMPLEMENT ME + q = [] + path = [user_id] + q.append(path) + # result = {} + + while len(q) > 0: + # print("queue", q) + new_path = q.pop(0) + vertex = new_path[-1] + # print("vertex", vertex) + + if vertex not in visited: + visited[vertex] = new_path + # if vertex == start_vertex: + # result[start_vertex] = new_path + # if len(self.get_friends(vertex)) == 0: + # all_paths.append(new_path) + # else: + for neighbor in self.get_friends(vertex): + # if neighbor in new_path: + # result[vertex] = new_path + # else: + copy_path = new_path + [neighbor] + q.append(copy_path) return visited if __name__ == '__main__': sg = SocialGraph() - sg.populate_graph(10, 2) - print(sg.friendships) + num_users = 1000 + avg_friendships = 5 + sg.populate_graph(num_users, avg_friendships) + # print(sg.friendships) + # print("get friends", sg.get_friends(1)) + # print(sg.bft_path(1)) connections = sg.get_all_social_paths(1) - print(connections) + # print(connections) + +# percentage of other users in extended social network + # NUMBER OF people we visited/ total number of peope + print(len(connections)/num_users) + +# average degree of separation --> average steps we took to visit someone +# average length of the path + # total_path_lenghths = 0 + + # for key, value in connections.items(): + # total_path_lenghths += len(value) + + # average_path_length = total_path_lenghths/len(connections) + # print(average_path_length) + + + + #New populate graph + + def populate_graph2(self, num_users, avg_friendships): + self.last_id = 0 + self.users = {} + self.friendships = {} + + # add users + for user in range(num_users): + self.add_user(user) + + #create friendships + #if 1 is a friend of 2, and 2 is a friend of 1, count + total_friendships = avg_friendships * num_users + friendships_made = 0 + + #do this until we have as many as we want + while friendships_made < total_friendships: + #choose two random user ids + first_user = random.randint(1, num_users) + second_user = random.randint(1, num_users) + #try and make a friendship + is_new_friendship = self.add_friendship(first_user, second_user) + if is_new_friendship: + friendships_made += 2