MCL summer workshop in graph theory

Lab 6/29/16

At this point you have several options for what to work on:

Exercise 1: Implementing BFS

Recall that BFS is an algorithm that calculates the distance from a single start vertex $s$ to every other vertex.

Write a function BFS($G$ , $s$ ) which performs a BFS in $G$ starting at $s$. Assume that the vertices of $G$ are 0, 1, 2, ... It should return a list whose $i$th element is the distance from $s$ to $i$. You can use -1 or None (the Python equivalent of null in Java) to signify vertices whose distances have not been set.

For example, for $K_3$ BFS starting at vertex 0 should return [0, 1, 1], since vertex 0 is distance 0 from itself and distance 1 from vertices 1 and 2.

Use the CompleteGraph, Path, and RandomGraph functions from last time for testing. If you didn't finish that part of the lab, or if you're afraid you didn't do it correctly, I added those functions to graphs.py so you can use them. You can download the latest version here.

In [ ]:
from graphs import *

def BFS(G, s):
    
    n = G.numVertices()
    dists = [-1 for i in range(n)]
    
    # distance from s to itself is 0
    dists[s] = 0
    
    toVisit = [s]
    
    for d in range(n):
        
        ##### YOUR CODE HERE #####
        
        # For each vertex at distance d
            # Mark all unmarked neighbors as distance d + 1
            # Hint: G.neighbors(v) gives you a list of v's neighbors
               
    return dists

###### Testing ######
n = 10
G = CompleteGraph(n) # Try changing this to Path, Cycle, and RandomGraph
startVertex = 0
L = BFS(G, startVertex)

print("Graph:")
print(G)
print("Start vertex: " + str(startVertex))
print("Vertex distances: " + str(L))

Exercise 2: Weighted coins

You can skip this exercise if you are using my updated graphs.py.

Write a function weightedCoin( $p$ ) which returns True with probability $p$, False with probability $1 - p$.

Hint: use the function random.rand(), which returns a random decimal between 0 and 1. The probability that it is between 0 and $p$ is $p$...

In [ ]:
import random

def weightedCoin(p):
    
    # Your code here
    
    return False

#### Testing ####

p = .2
n = 100

# Flip n coins; keep track of number of heads
print("Flipping " + str(n) + " coins...")
numHeads = 0
for i in range(n):
    if weightedCoin(p):
        numHeads += 1

print("Proportion that came up heads: " + str(numHeads / n) + " (should be approx. " + str(p) + ")" )

Now modify your RandomGraph function from last time to add edges with probability $p$ instead of $1/2$. Again, be careful not to flip coins for both $uv$ and $vu$!

In [ ]:
from graphs import *

def RandomGraph(n, p):
    
    # Your code here
    
###### Testing ######

print(RandomGraph(10))

Exercise 3: One connected component

Write a function connectedComponent( $G, v$ ) which returns a list of vertices in the same connected component as $v$.

Hint: use BFS from Exercise 1.

In [ ]:
from graphs import *

def connectedComponent(G, v):
    
    CC = [v] # v is always in its own connected component
    
    # Your code here
    
    return CC

###### Testing ######

n = 10
G = RandomGraph(n, .2) 
startVertex = 0
L = connectedComponent(G, startVertex)

print("Graph:")
print(G)
print("Start vertex: " + str(startVertex))
print("Connected component: " + str(L))

Exercise 4: Connected components

Now write a function connectedComponents( $G$ ) which returns all connected components of $G$. It should return a list of lists, with each inner list being one of the connected components.

Hint: use Exercise 3 to find the connected components one by one!

In [ ]:
from graphs import *

def connectedComponents(G):
    CCs = []
    
    # Your code here
    
    return CCs

######## Testing ########

n = 10
G = RandomGraph(n, .2) 
L = connectedComponents(G)

print("Graph:")
print(G)
print("Connected components: " + str(L))

Exercise 5: Matrix multiplication

You can use a 2D list A to represent a matrix $A$ in Python, with A[i][j] corresponding to $a_{ij}$. Write a function mult( $A, B$ ) which multiplies $A$ and $B$ (using matrix multiplication) and returns the resulting matrix. You can assume that $A$ and $B$ are both $n \times n$, if that makes life easier.

In [ ]:
def mult(A, B):
    n = len(A)
    
    # Start with an n x n matrix of 0s
    AB = [[0 for j in range(n)] for i in range(n)]
    
    for i in range(n):
        for j in range(n):
            
            # Calculate the correct value of AB[i][j]
    
    return AB

###### Testing ######

def printMatrix(A):
    """Pretty print a matrix A"""
    for row in A:
        print(row)
    print("")
        
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
B = [[1, -1, 1], [-1, 1, 1], [1, -1, 1]]
print("A = ")
printMatrix(A)
print("B = ")
printMatrix(B)
print("AB = ")
printMatrix(mult(A, B))
print("BA = ")
printMatrix(mult(B, A))

Exercise 6: Adjacency matrices

Definition. Let $G$ be a graph on $n$ vertices. The adjacency matrix of $G$ is an $n \times n$ matrix $A$ such that $a_{ij}$ is the number of edges from vertex $i$ to vertex $j$

Draw the graph represented below and then write down its adjacency matrix.

In [34]:
print(RandomGraph(10, .25))
0: [8, 1, 3, 7]
1: [0]
2: [6]
3: [0, 9, 5, 6]
4: [5]
5: [3, 4]
6: [2, 3]
7: [0, 8]
8: [0, 7]
9: [3]

Now write a function that returns the adjacency matrix of a graph $G$. Since our Graph class does not allow multiple edges, all entries will be 0 or 1.

In [ ]:
from graphs import *

def adjacencyMatrix(G):
    n = G.numVertices()
    A = [[0 for j in range(n)] for i in range(n)]
    
    # Your code here
    
    return A

###### Testing ######

G = CompleteGraph(10) # Try with Path, Cycle, RandomGraph, etc.
print("Graph:")
print(G)
print("Adjacency matrix:")
printMatrix(adjacencyMatrix(G))