Skip to content

Graphs #756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .vs/Graphs/v16/.suo
Binary file not shown.
10 changes: 10 additions & 0 deletions .vs/VSWorkspaceState.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"ExpandedNodes": [
"",
"\\projects",
"\\projects\\ancestor",
"\\projects\\graph"
],
"SelectedNode": "\\projects\\ancestor\\ancestor.py",
"PreviewInSolutionExplorer": false
}
Binary file added .vs/slnx.sqlite
Binary file not shown.
47 changes: 46 additions & 1 deletion projects/ancestor/ancestor.py
Original file line number Diff line number Diff line change
@@ -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

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
142 changes: 90 additions & 52 deletions projects/graph/graph.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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))
112 changes: 74 additions & 38 deletions projects/social/social.py
Original file line number Diff line number Diff line change
@@ -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)