# CSPT10 Graphs IV
```
from collections import deque
import random
import math
import time
class User:
def __init__(self, name):
self.name = name
class SocialGraph:
def __init__(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")
elif friend_id in self.friendships[user_id] or user_id in self.friendships[friend_id]:
print("WARNING: Friendship already exists")
else:
self.friendships[user_id].add(friend_id)
self.friendships[friend_id].add(user_id)
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.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.
The number of users must be greater than the average number of friendships.
"""
self.last_id = 0
self.users = {}
self.friendships = {}
for i in range(0, num_users):
self.add_user(f"User {i}")
possible_friendships = []
# Generate all possible friendships possible
for user_id in self.users:
# To avoid duplicating friendships, create friendships from user_id + 1
for friend_id in range(user_id + 1, self.last_id + 1):
possible_friendships.append((user_id, friend_id))
# Shuffle the entire array of possible friendships
random.shuffle(possible_friendships)
# Select the first num_users * avg_friendships / 2
# We / 2 because a friendship is a bidirectional edge (we're essentially adding two edges)
for i in range(0, math.floor(num_users * avg_friendships / 2)):
friendship = possible_friendships[i]
self.add_friendship(friendship[0], friendship[1])
def populate_graph_linear(self, num_users, avg_friendships):
# Keep randomly making friendships until we've made the right amount
# randomly select two vertices to become friends
# if it's a success, then increment number of friendships made
# else try again
self.last_id = 0
self.users = {}
self.friendships = {}
for i in range(0, 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_linear(user_id, friend_id):
total_friendships += 2
else:
collisions += 1
print(f"collisions: {collisions}")
# returns true if making friendship was a success
def add_friendship_linear(self, user_id, friend_id):
if user_id == friend_id:
return False
# we don't wanna make a friendship if it already exists
elif friend_id in self.friendships[user_id] or user_id in self.friendships[friend_id]:
return False
else:
self.friendships[user_id].add(friend_id)
self.friendships[friend_id].add(user_id)
return True
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.
"""
visited = {} # a dictionary mapping from node id --> [path from user_Id]
queue = deque() # we need this for a bft
queue.append([user_id])
while len(queue) > 0:
currPath = queue.popleft()
currNode = currPath[-1]
visited[currNode] = currPath # bft guarantees us that this is the shortest path to currNode from user_id
for friend in self.friendships[currNode]:
if friend not in visited:
newPath = currPath.copy()
newPath.append(friend)
queue.append(newPath)
return visited
if __name__ == '__main__':
sg = SocialGraph()
start_time = time.time()
sg.populate_graph(1000, 5)
end_time = time.time()
print (f"runtime: {end_time - start_time} seconds")
connections = sg.get_all_social_paths(1)
# print(sg.friendships)
# print(connections)
total = 0
for user_id in connections:
total += len(connections[user_id]) - 1
print(len(connections))
print(total / len(connections))
total_connections = 0
total_degrees = 0
iterations = 10
for i in range(0, iterations):
sg.populate_graph(1000, 5)
connections = sg.get_all_social_paths(1)
total = 0
for user_id in connections:
total += len(connections[user_id]) - 1
total_connections += len(connections)
total_degrees += total / len(connections)
print("-----")
print(f"Friends in network: {len(connections)}")
print(f"Avg degrees: {total / len(connections)}")
print(total_connections / iterations)
print(total_degrees / iterations)
```