Skip to content

Instantly share code, notes, and snippets.

@dhammack
Last active December 30, 2015 07:59
Show Gist options
  • Save dhammack/7799136 to your computer and use it in GitHub Desktop.
Save dhammack/7799136 to your computer and use it in GitHub Desktop.
To study for the DSA final, I'm implementing all the algorithms we've covered since exam 2. This is an ipython notebook of them.
{
"metadata": {
"name": "DSA Exam Study"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": "# Implementations and explainations of different algorithms #\n\nTo study for the exam, I'm going to go through the powerpoints and implement the algorithms, along with explainations. \n\n### Implemented in this workbook: ###\n\n#### Graphs ####\n\n\n* Directed Graph\n* Undirected Graph\n* Breadth first search\n* Depth first search\n* Find all connected components (undirected only)\n* Best path (Dijkstra's algorithm)\n* Cycle detection\n* Kruskal's minimal cost spanning tree algoritm\n\n\n#### Divide and Conquer ####\n\n* minimum of unsorted list\n* tiling a defective chessboard (no code)\n* using queues instead of recursion (general principle)\n* merge sort\n* non recursive merge sort (uses queue idea)\n* natural merge sort\n* quick sort\n* in place quick sort (**hard**)\n* rank finding (find kth smallest element in unsorted list)\n* closest pair of points (**hard**)\n\n#### Greedy Methods ####\n\n* 0/1 Knapsack greedy profit density heuristic (**tricky**)\n\n#### Old material ####\n\n* LZW Compression\n* Hash Table\n\n\n#### In progress ####\n\n* AVL trees\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "from Queue import Queue\n%pylab inline\n\nclass WeightedDirectedGraph(object):\n \"\"\"\n Implementation of a graph, using an adjacency map instead of a matrix.\n \n \"\"\"\n def __init__(self):\n \"\"\"\n Creates a weighted directed graph. In other words, there is a collection of nodes\n and edges. Each edge goes from one node to another but not necessarily in reverse. \n Each node also has a weight associated with it.\n \"\"\"\n self.A = {}\n \n def AddNode(self, node_num):\n \"\"\"\n Adds a node to the graph.\n \"\"\"\n #check that the node isn't already here\n if node_num in self.A:\n print 'Node is already in graph'\n return\n \n self.A[node_num] = []\n return\n \n def AddEdge(self, start, end, weight):\n \"\"\"\n Add an edge to the graph, given the start, end and weight.\n \"\"\"\n #add an edge\n if start not in self.A:\n print 'Add a node before connecting it'\n return\n \n self.A[start].append((end, weight))\n return\n \n def ConnectedNodes(self, start):\n \"\"\"\n To find the connected nodes for a given node, we use the inner adjacency map (sparse matrix).\n The map takes the node id and returns the list of all outgoing connections and weights.\n \"\"\"\n #returns all connected nodes\n if start not in self.A: return []\n return [node for (node,weight) in self.A[start] if node != start]\n \n def BFS(self, start):\n \"\"\"\n Returns a list of nodes, in order as visited from a BFS starting at start\n \"\"\"\n found, visited, Q = [], set([start]), Queue()\n Q.put(start)\n while not Q.empty():\n current = Q.get()\n found.append(current)\n #explore around.\n for neighbor in self.ConnectedNodes(current):\n if neighbor not in visited:\n Q.put(neighbor)\n visited.add(neighbor)\n return found\n \n def DFS(self, start, visited=None):\n \"\"\"\n Traverses the graph with depth-first search, recursively.\n \"\"\"\n #don't visit this node again\n if visited is None: visited=set()\n visited.add(start)\n \n #recrusive step: concatenate DFS of each neighbor\n traversal = [start]\n for neighbor in self.ConnectedNodes(start):\n if neighbor in visited: continue\n traversal.extend(self.DFS(neighbor, visited))\n #if we hit the base case, then we'll skip the loop\n return traversal\n \n def CanReach(self, start, end):\n \"\"\"\n Returns true if you can reach the end from the start.\n \"\"\"\n return end in self.DFS(start) #that was easy\n \n def PathDijkstra(self, start, end):\n \"\"\"\n Uses Dijkstra's greedy algorithm to find a path from start to end.\n Returns the path, but it finds paths to all nodes. It could be modified\n to quit once 'end' is visited, if desired.\n Returns a tuple (path, dist).\n \"\"\"\n #Breadth-first-search with a greedy heuristic\n prev = {start:start} #given a node, it finds the previous one in the path\n dist = {node:sys.maxint for node in self.A.iterkeys()} #holds distance from start to each node in path\n dist[start] = 0 #start->start is zero\n visited = set()\n current = start\n while len(visited) < len(self.A):\n #find the node with the smallest dist not yet visited\n current = min((node for node in dist.iterkeys() if node not in visited),\n key = lambda x: dist[x])\n #mark this node as visited\n visited.add(current)\n #check neighbors\n for neighbor in self.ConnectedNodes(current):\n dist_to_neighbor = next(dist for node,dist in self.A[current] if node == neighbor)\n if dist[current]+dist_to_neighbor < dist[neighbor]:\n #this path is better, so take it\n dist[neighbor] = dist[current]+dist_to_neighbor\n prev[neighbor] = current\n \n #now just backtrace to get the path\n path = []\n current = end\n while prev[current] != current:\n path.append(current)\n current = prev[current]\n \n return ([start] + path[::-1], dist[end])\n ",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "Populating the interactive namespace from numpy and matplotlib\n"
}
],
"prompt_number": 2
},
{
"cell_type": "markdown",
"metadata": {},
"source": "So now let's build up a graph and test it out."
},
{
"cell_type": "code",
"collapsed": false,
"input": "G = WeightedDirectedGraph()\nG.AddNode('a')\nG.AddNode('b')\nG.AddEdge('a','b',10)\nG.AddNode('c')\nG.AddEdge('b','c',10)\nG.AddNode('d')\nG.AddEdge('b','d',10)\nG.AddNode('e')\nG.AddEdge('a','e',10)\nprint 'breadth first search from a', G.BFS('a')\nprint 'breadth first search from b', G.BFS('b')\nprint 'breadth first search from e', G.BFS('e')",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "breadth first search from a ['a', 'b', 'e', 'c', 'd']\nbreadth first search from b ['b', 'c', 'd']\nbreadth first search from e ['e']\n"
}
],
"prompt_number": 3
},
{
"cell_type": "markdown",
"metadata": {},
"source": "It's hard to tell if the results are correct, so I'm going to use a neat library for graphs: networkx"
},
{
"cell_type": "code",
"collapsed": false,
"input": "import networkx as nx\n%pylab inline\nG2 = nx.DiGraph()\nG2.add_edge('a','b')\nG2.add_edge('b','c')\nG2.add_edge('b','d')\nG2.add_edge('a','e')\n#draw\nnx.draw_spectral(G2)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "Populating the interactive namespace from numpy and matplotlib\n"
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\n",
"text": "<matplotlib.figure.Figure at 0x6377240>"
}
],
"prompt_number": 4
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Now it's easy to see the connections. We can also see that the BFS was correct."
},
{
"cell_type": "code",
"collapsed": false,
"input": "print [i for i in nx.bfs_edges(G2, 'a')]",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "[('a', 'b'), ('a', 'e'), ('b', 'c'), ('b', 'd')]\n"
}
],
"prompt_number": 5
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Lets try to build the graph specified by the homework assignment, and then check our Dijkstra's algorithm."
},
{
"cell_type": "code",
"collapsed": false,
"input": "G = WeightedDirectedGraph()\n#add nodes\nfor i in range(1,7):\n G.AddNode(i)\n#add edges\nG.AddEdge(1,2,10)\nG.AddEdge(1,4,30)\nG.AddEdge(1,5,100)\nG.AddEdge(2,3,50)\nG.AddEdge(3,6,5)\nG.AddEdge(3,5,10)\nG.AddEdge(4,3,20)\nG.AddEdge(4,6,15)\nG.AddEdge(5,4,60)\n\n#test dijkstra\nprint G.PathDijkstra(1, 6)\nprint G.DFS(1)\nprint G.BFS(1)\nprint G.CanReach(1,6)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "([1, 4, 6], 45)\n[1, 2, 3, 6, 5, 4]\n[1, 2, 4, 5, 3, 6]\nTrue\n"
}
],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": "We can also draw this graph to confirm that it's correct.\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "G2 = nx.DiGraph()\nG2.add_weighted_edges_from([(1,2,10), (1,4,30),(1,5,100)])\nG2.add_weighted_edges_from([(2,3,50), (3,6,5), (3,5,10)])\nG2.add_weighted_edges_from([(4,3,20), (4,6,15), (5,4,60)])\n#draw\nnx.draw_circular(G2)",
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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\ntl91/Qvgr2yWFStWFHdQlTXP+aqk5Lq6+HX3bvaPjk55N2ohp4Cl1dUcPHqURYsWFWs8lYnBwUFe\nbmvj64sXuZmTuleA1nSa1u5uXtu6tVjj6Q7gJ1+VlB07d/JtUxNvVFVNe653wu/Aqpoatmzfbnh1\nQ1paWli1di1Pp9P/PZRlOv8AG5NJxpubeXXzzTyUUrqW8VVJyWazfH7wIJ82NfFKMsnp66wdB44A\nj6XTPLFhA7lt24Km1O2uoqKCd3p7Wbh6NS3pNMenWf8T8Ex1Nd8vXsxH/f3eUa8ZM74qOQ0NDRw6\ndozK9nYeTKXoSKcZJP+Yv2HgR2APsCST4fmGBl7v7eWtnh5/KEE3ZdasWXzQ10dndzdP1tayLJul\nj/yNexfIb2X0A0/V1LA0nWbhunUMHD7MnDlz/te5VR7c81VJGx4eZv++fXy4axenzpxhZHSU2kyG\nJc3NbMzlWLlyJZWV/g+pmRkbG+PAgQO839PDdydOcGFkhEwqxb0LFvBiVxcdHR0+MU23lPGVJCmY\nHxkkSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8\nJUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJ\nCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpm\nfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwl\nSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkKZnwlSQpmfCVJCmZ8JUkK\nZnwlSQpmfCVJCvYv2GGg+sm3ebcAAAAASUVORK5CYII=\n",
"text": "<matplotlib.figure.Figure at 0x8686b00>"
}
],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": "And the shortest path..."
},
{
"cell_type": "code",
"collapsed": false,
"input": "print nx.shortest_path(G2, source=1, target=6)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "[1, 4, 6]\n"
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Let's implement a weighted undirected graph. I'll do this by just duplicating connections!"
},
{
"cell_type": "code",
"collapsed": false,
"input": "class WeightedUndirectedGraph(object):\n \"\"\"\n Implementation of a weighted, undirected graph. Uses an adjacency map (sparse matrix).\n \n \"\"\"\n def __init__(self):\n \"\"\"\n Creates a weighted undirected graph. In other words, there is a collection of nodes\n and edges. Each edge is symmetric.\n \"\"\"\n self.A = {}\n \n def AddNode(self, node_num):\n \"\"\"\n Adds a node to the graph.\n \"\"\"\n #check that the node isn't already here\n if node_num in self.A:\n print 'Node is already in graph'\n return\n \n self.A[node_num] = []\n return\n \n def AddEdge(self, start, end, weight):\n \"\"\"\n Add an edge to the graph connecting the two specified nodes\n \"\"\"\n #add an edge\n if start not in self.A:\n print 'Add a node before connecting it'\n return\n \n #in an undirected graph, we need connections going both ways!\n self.A[start].append((end, weight))\n self.A[end].append((start, weight))\n return\n \n def RemoveEdge(self, start, end):\n \"\"\"\n Removes an edge from the graph.\n \"\"\"\n self.A[start] = [(node,weight) for (node,weight) in self.A[start] if node!=end]\n self.A[end] = [(node,weight) for (node,weight) in self.A[end] if node!=start]\n return\n \n def ConnectedNodes(self, start):\n \"\"\"\n To find the connected nodes for a given node, we use the inner adjacency map (sparse matrix).\n The map takes the node id and returns the list of all outgoing connections and weights.\n \"\"\"\n #returns all connected nodes\n if start not in self.A: return []\n return [node for (node,weight) in self.A[start] if node != start]\n \n def BFS(self, start):\n \"\"\"\n Returns a list of nodes, in order as visited from a BFS starting at start\n \"\"\"\n found, visited, Q = [], set([start]), Queue()\n Q.put(start)\n while not Q.empty():\n current = Q.get()\n found.append(current)\n #explore around.\n for neighbor in self.ConnectedNodes(current):\n if neighbor not in visited:\n Q.put(neighbor)\n visited.add(neighbor)\n return found\n \n def DFS(self, start, visited=None):\n \"\"\"\n Traverses the graph with depth-first search, recursively.\n \"\"\"\n #don't visit this node again\n if visited is None: visited=set()\n visited.add(start)\n \n #recrusive step: concatenate DFS of each neighbor\n traversal = [start]\n for neighbor in self.ConnectedNodes(start):\n if neighbor in visited: continue\n traversal.extend(self.DFS(neighbor, visited))\n #if we hit the base case, then we'll skip the loop\n return traversal\n \n def CanReach(self, start, end):\n \"\"\"\n Returns true if you can reach the end from the start.\n \"\"\"\n return end in self.DFS(start) #that was easy\n \n def CountConnectedComponents(self):\n \"\"\"\n Finds the number of connected components. A connected component is a subgraph\n which has a path between each pair of nodes. \n \"\"\"\n visited = set()\n num = 0\n while len(visited) < len(self.A):\n #get a node which isn't yet visited\n current = next(node for node in self.A.iterkeys() if node not in visited)\n #update the visited set with all the nodes reachable from here\n visited.update(self.BFS(current))\n num += 1\n return num\n \n def display(self):\n #use networkx to display this graph\n G = nx.Graph()\n for start,outgoing in self.A.iteritems():\n G.add_weighted_edges_from([(start, end, weight) for (end,weight) in outgoing])\n \n nx.draw_circular(G)\n show()\n \n def ConnectedComponents(self):\n \"\"\"\n Returns a list of connected components - each of which is a graph.\n \"\"\"\n N = [node for node in self.A.iterkeys()] #list of nodes\n subgraphs = []\n while len(N) > 0:\n connected = self.DFS(N[0])\n G = WeightedUndirectedGraph()\n for node,connections in self.A.iteritems():\n if node in connected:\n G.A[node] = connections\n \n subgraphs.append(G)\n N = [node for node in N if node not in connected]\n return subgraphs\n \n def HasCycles(self):\n \"\"\"\n Uses DFS to determine if the graph has cycles.\n If there are more than one connected components, then it does DFS on each.\n While searching depth-first, if we hit any node twice then there is a cycle!\n \"\"\"\n if self.CountConnectedComponents() == 1:\n #number of edges is double (we duplicate connections in a undirected graph)\n edge_count = sum(len(outgoing) for outgoing in self.A.itervalues())/2\n return edge_count > len(self.A) - 1\n\n return any([subGraph.HasCycles() for subGraph in self.ConnectedComponents()])\n \n \n def KruskalsMethod(self):\n \"\"\"\n Uses Kruskal's method to find a minimum cost spanning tree.\n Returns: an undirected graph: the spanning tree\n \"\"\"\n \n #start with a forest which has no edges\n T = WeightedUndirectedGraph()\n for start in self.A.iterkeys():\n T.AddNode(start)\n\n edge_list = [(a,b,w) for a,outgoing in self.A.iteritems() for (b,w) in outgoing]\n while T.CountConnectedComponents() > 1:\n #Be greedy on edge cost - add the least cost edge\n (start,end,weight) = min((edge for edge in edge_list),\n key = lambda x: x[2])\n\n #see if adding this would cause a cycle\n comps = T.CountConnectedComponents()\n T.AddEdge(start, end, weight)\n \n #remove the edges from the candidates\n edge_list = [(s,e,w) for (s,e,w) in edge_list \n if (s!=start or e!=end) and (s!=end or e!=start)]\n \n if T.CountConnectedComponents() == comps:\n #we didn't connect anything - we must have created a cycle!\n T.RemoveEdge(start, end)\n continue\n #if we didn't add a cycle, then keep going!\n return T\n \n \n def PathDijkstra(self, start, end):\n \"\"\"\n Uses Dijkstra's greedy algorithm to find a path from start to end.\n Returns the path, but it finds paths to all nodes. It could be modified\n to quit once 'end' is visited, if desired.\n Returns a tuple (path, dist).\n \"\"\"\n #Breadth-first-search with a greedy heuristic\n prev = {start:start} #given a node, it finds the previous one in the path\n dist = {node:sys.maxint for node in self.A.iterkeys()} #holds distance from start to each node in path\n dist[start] = 0 #start->start is zero\n visited = set()\n current = start\n while len(visited) < len(self.A):\n #find the node with the smallest dist not yet visited\n current = min((node for node in dist.iterkeys() if node not in visited),\n key = lambda x: dist[x])\n #mark this node as visited\n visited.add(current)\n #check neighbors\n for neighbor in self.ConnectedNodes(current):\n dist_to_neighbor = next(dist for node,dist in self.A[current] if node == neighbor)\n if dist[current]+dist_to_neighbor < dist[neighbor]:\n #this path is better, so take it\n dist[neighbor] = dist[current]+dist_to_neighbor\n prev[neighbor] = current\n \n #now just backtrace to get the path\n path = []\n current = end\n while prev[current] != current:\n path.append(current)\n current = prev[current]\n \n return ([start] + path[::-1], dist[end])\n ",
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 9
},
{
"cell_type": "code",
"collapsed": false,
"input": "G = WeightedUndirectedGraph()\n#add nodes\nfor i in range(1,7):\n G.AddNode(i)\n#add edges\nG.AddEdge(1,2,10)\nG.AddEdge(1,4,30)\nG.AddEdge(1,5,100)\nG.AddEdge(2,3,50)\nG.AddEdge(3,6,5)\nG.AddEdge(3,5,10)\nG.AddEdge(4,3,20)\nG.AddEdge(4,6,15)\nG.AddEdge(5,4,60)\n\n#new\nG.AddNode('a')\nG.AddNode('b')\nG.AddEdge('a','b',10)\n\n#tests\nprint G.PathDijkstra(1, 6)\nprint G.DFS(1)\nprint G.BFS(1)\nprint G.CanReach(1,6)\nprint G.CanReach(1, 'a')\nprint G.CountConnectedComponents()\nG.display()\n",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "([1, 4, 6], 45)\n[1, 2, 3, 6, 4, 5]\n[1, 2, 4, 5, 3, 6]\nTrue\nFalse\n2\n"
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\ngg==\n",
"text": "<matplotlib.figure.Figure at 0x8700898>"
}
],
"prompt_number": 10
},
{
"cell_type": "code",
"collapsed": false,
"input": "#we can display all the subgraphs\nfor subG in G.ConnectedComponents():\n subG.display()",
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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\n",
"text": "<matplotlib.figure.Figure at 0x8807ba8>"
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\nfq43xT9AkwwZCBw+XIpXpEmdOnVo4u9PSxcXotP4Gj3QO316lE8+oU+/fsaMJ2yAlK8wKxkzZmTj\nnj2sLVSI7unTcyeV5yrAAaC6iwuf9ezJ4GHDTJRSWDo7OzuCpkyhaPPm1HFx4cRbnn8VaOPszNny\n5f+vnft1iQOM4zj+UctxdyLGA8NYWdXVVeNYFC4MxjAtmUxLhqWrChsD2cC2JghLCzPbhnVlxXQW\nQTAs+BcM4aO41+sveNr7+fHlybeTExP13Jn48uBMJpP8PDvL4tZWng0GmQ6H+ZHbb/7mSX4n+ZRk\nYzzOm8kk7/f382E2y8LCv1xU879bWlrK56OjvN7by8vV1bxYXs5Rbgf3LnP7lHGS5NVolOfDYZ5u\nb+f76WlWVlbudd08Dt58edDm83m+HB7m68FB/lxc5Or6OqvjcTbW1/Nudzebm5tZXLSH5G5ubm5y\nfHycj7NZfp2f5/LqKuPBIE/W1vJ2ZyfT6TSj0ei+l8kjIr4AUObIAABl4gsAZeILAGXiCwBl4gsA\nZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl\n4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXi\nCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeIL\nAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZeILAGXiCwBl4gsAZX8BvY6cPmBSH+UA\nAAAASUVORK5CYII=\n",
"text": "<matplotlib.figure.Figure at 0x6377208>"
}
],
"prompt_number": 11
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Let's try out the spanning tree algorithms"
},
{
"cell_type": "code",
"collapsed": false,
"input": "G = WeightedUndirectedGraph()\n#add nodes\nfor i in range(1,7):\n G.AddNode(i)\n#add edges\nG.AddEdge(1,2,10)\nG.AddEdge(1,4,30)\nG.AddEdge(1,5,100)\nG.AddEdge(2,3,50)\nG.AddEdge(3,6,5)\nG.AddEdge(3,5,10)\nG.AddEdge(4,3,20)\nG.AddEdge(4,6,15)\nG.AddEdge(5,4,60)\n\nprint 'G has cycles:', G.HasCycles()\n\nT = G.KruskalsMethod()\nT.display()\nprint 'T has cycles:', T.HasCycles()",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "G has cycles: True\n"
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\nj/LSa6+xr3hx/ufnd93P9V70F9AsZ04iR45U8coNadCgAc0iImgVHEziDT7GBfTPkQOjUiUGDBrk\nzniSDah8xaPkypWLpevXs7B4cXrnyMEf1zjWALYAtYKDebxvX4a/8IJJKcXuHA4H0RMmUOrJJ2kQ\nHMye6xz/I9AmKIhDFSsyf9kyTdTLbVP5iscpXLgwm+Li8GnblvsCA2kfHMw6Mi/zdwY4BnwIhIaE\n0LVwYV6cMIE333lH3+krN8XX15cpMTF0GjWKJ/LmpXauXMSQObh3lsxTGcuAFjlzEhYcTKmePVmx\neTO5c+e2NLd4B53zFY925swZZkyfzsyJEzl+4gRJFy6QNySE0EqV6D98OI888gg+PnoPKbcnPT2d\nxYsXM/mdd/j2wAHOJiUREhhIiWLF6D50KO3btydnzpxWxxQvovIVERExmZYMIiIiJlP5ioiImEzl\nKyIiYjKVr4iIiMlUviIiIiZT+YqIiJhM5SsiImIyla+IiIjJVL4iIiImU/mKiIiYTOUrIiJiMpWv\niIiIyVS+IiIiJlP5ioiImEzlKyIiYjKVr4iIiMlUviIiIiZT+YqIiJhM5SsiImIyla+IiIjJVL4i\nIiImU/mKiIiYTOUrIiJiMpWviIiIyVS+IiIiJlP5ioiImEzlKyIiYjKVr4iIiMlUviIiIiZT+YqI\niJhM5SsiImIyla+IiIjJVL4iIiImU/mKiIiYTOUrIiJiMpWviIiIyVS+IiIiJlP5ioiImEzlKyIi\nYjKVr4iIiMlUviIiIiZT+YqIiJhM5SsiImIyla+IiIjJVL4iIiImU/mKiIiYTOUrIiJiMpWviIiI\nyVS+IiIiJlP5ioiImEzlKyIiYjKVr4iIiMlUviIiIiZT+YqIiJhM5SsiImIyla+IiIjJVL4iIiIm\nU/mKiIiYTOUrIiJisv8HjY0HcOrZvvwAAAAASUVORK5CYII=\n",
"text": "<matplotlib.figure.Figure at 0x88367b8>"
},
{
"output_type": "stream",
"stream": "stdout",
"text": "T has cycles: False\n"
}
],
"prompt_number": 12
},
{
"cell_type": "markdown",
"metadata": {},
"source": "That's all the algorithms covered in the lecture slides, excluding Solin's and Prim's spanning tree algorithms which only had one slide each.\n\n## Divide and Conquer Algorithms ##\n\nDivide and conquer is a nice paradigm for solving problems. Small problems are solved directly and big problems are just multiple smaller problems. Thus it fits a recursive paradigm very well.\n\n###1 - Finding the min of an unsorted list###\n\nIf the list is size 1, then that element is the min.\nIf the list is bigger, the min is the minimum of the two sub-mins."
},
{
"cell_type": "code",
"collapsed": false,
"input": "def divide_conquer_min(iterable):\n #base case\n if len(iterable) == 1:\n return iterable[0]\n \n #recursive step\n N = len(iterable)\n m_1,m_2 = divide_conquer_min(iterable[:N/2]), divide_conquer_min(iterable[N/2:])\n return m_1 if m_1 < m_2 else m_2\n\n#let's test it out\nelems = sorted(range(20),key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint 'min of list:', divide_conquer_min(elems)\n",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\nmin of list: 0\n"
}
],
"prompt_number": 13
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###2 - Tiling a defective chessboard ###\n\nYeah, I'm not writing code to do this because it's not really a problem which needs to be solved. In case he asks it on the exam, here's the rundown:\n\nA triomino (as opposed to a domino) is three squares making up a right angle. If you have a 2x2 board with one spot missing, you can fill the board with a triomino. This is the direct solution to a small problem.\n\nFor a bigger board, we're going to break it up into four subsquares, but first we need to make them defective so that the subproblems are similar to the big problem. Note that if we divide up the chessboard into four equal sized squares, one of them must have a defective spot - that's how our problem was defined. To make the other three defective, we place a triomino such that it leaves the defective one unchanged and covers one square of the nondefective subboards. Then we cover each recursively.\n\n"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###3 - Finding max and min simultaneously###\n\nTrivial extension of the min algorithm"
},
{
"cell_type": "markdown",
"metadata": {},
"source": "##Side note##\n\nDivide and conquer algorithms form a tree-like structure if you trace the subproblems. In the recursive implementation above, the traversal is similar to that of depth-first-search (using the call stack as a stack). In other words, the algorithm keeps trying to go down the tree until hitting a base case (leaf), and then goes up. We can change the traversal to be similar to breadth-first search by using a queue. Remember the initialization algorithm for leftist trees? It's similar to that. Here's the min algorithm using a breadth first manner, using a queue."
},
{
"cell_type": "code",
"collapsed": false,
"input": "def BF_min(iterable):\n #initialize the queue\n Q = Queue()\n for i in iterable:\n Q.put(i)\n \n #compute the min\n while not Q.empty():\n #pull two items off\n item1 = Q.get()\n if Q.empty():\n return item1\n item2 = Q.get()\n #put the minimum back in\n Q.put(item1 if item1 < item2 else item2)\n \n\n#let's test it out\nelems = sorted(range(20), key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint 'min of list:', BF_min(elems)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\nmin of list: 0\n"
}
],
"prompt_number": 14
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###4 - Merge Sort###\n\nIf a list has one element, it is sorted (base case). If it has more than that, divide it up into two (close to) equal sized sublists and sort those. After, combine the sorted lists."
},
{
"cell_type": "code",
"collapsed": false,
"input": "def merge(L_1, L_2):\n final = []\n while len(L_1) > 0 or len(L_2) > 0:\n #we take from L_1 if L_2 is empty or L_1's first element is smaller\n if len(L_2) == 0 or L_1[0] < L_2[0]:\n final.append(L_1[0])\n #check if we're done\n if len(L_1) == 1:\n final.extend(L_2)\n return final\n L_1 = L_1[1:]\n else:\n final.append(L_2[0])\n #check if we're done\n if len(L_2) == 1:\n final.extend(L_1)\n return final\n L_2 = L_2[1:]\n\ndef merge_sort(iterable):\n #base case\n if len(iterable) <= 1:\n return iterable\n \n #recursive step\n N = len(iterable)\n L_1, L_2 = merge_sort(iterable[:N/2]), merge_sort(iterable[N/2:])\n \n #combine the two lists\n return merge(L_1, L_2)\n\n#let's test it out\nelems = sorted(range(20), key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint 'sorted list', merge_sort(elems)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\nsorted list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n"
}
],
"prompt_number": 15
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###5 - Non recursive merge sort###\n\nJust use the queue idea from earlier, starting as singleton lists."
},
{
"cell_type": "code",
"collapsed": false,
"input": "def nonrecursive_merge_sort(iterable):\n #initialize the queue\n Q = Queue()\n for i in iterable:\n Q.put([i]) #singleton lists\n \n #pull off two lists, merge, and put back in queue\n while not Q.empty():\n #pull two items off\n L_1 = Q.get()\n if Q.empty():\n return L_1\n L_2 = Q.get()\n \n #merge and put\n Q.put(merge(L_1, L_2))\n\n#let's test it out\nelems = sorted(range(20), key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint 'sorted list', nonrecursive_merge_sort(elems)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\nsorted list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n"
}
],
"prompt_number": 16
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Easy enough.\n\n###6 - Natural Merge Sort###\n\nThe goal of natural merge sort is to not mess up any parts of the lists which are already in order. Instead of starting at singleton lists, we start with sublists which are already sorted.\n\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "def natural_merge_sort(iterable):\n #initialize the queue\n Q = Queue()\n #add already sorted sublists\n sublist = [iterable[0]]\n for i in range(len(iterable)-1):\n if iterable[i+1] >= iterable[i]:\n sublist.append(iterable[i+1])\n else:\n Q.put(sublist)\n sublist = [iterable[i+1]]\n Q.put(sublist)\n \n #to see what it's doing\n qlist = [sublist for sublist in list(Q.queue)]\n print 'the queue:\\n', qlist\n print 'sorted list:'\n #pull off two lists, merge, and put back in queue\n while not Q.empty():\n #pull two items off\n L_1 = Q.get()\n if Q.empty():\n return L_1\n L_2 = Q.get()\n \n #merge and put\n Q.put(merge(L_1, L_2))\n\n#let's test it out\nelems = sorted(range(20), key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint natural_merge_sort(elems)\n",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\nthe queue:\n[[3, 4, 11], [10, 13], [12, 15], [14, 17], [16, 19], [18], [5, 6, 7, 8, 9], [0, 1, 2]]\nsorted list:\n[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n"
}
],
"prompt_number": 17
},
{
"cell_type": "markdown",
"metadata": {},
"source": "I added some code to print the queue before doing the merge sort - it shows how subgroups were formed of elements which were already ordered."
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###7 - Quick Sort###\n\nBase case: a list of size 1 is sorted. \n\nRecursive step: select a pivot, left group, right group. All elements of the left group are less than the pivot, and all elements of the right group are greater than the pivot. Quick sort each subgroup, then arrange in order: left, pivot, right.\n\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "def quicksort(iterable):\n #this is the base case\n if len(iterable) <= 1:\n return iterable\n \n #choose a pivot\n N = len(iterable)\n pivotInd = N/2\n pivot = iterable[pivotInd] #or iterable[0], whatever you feel like doing.\n \n #divide up the list\n left, right = [], []\n for i,item in enumerate(iterable):\n if item <= pivot and i!=pivotInd:\n left.append(item)\n elif item > pivot:\n right.append(item)\n \n #sort each half\n left,right = quicksort(left), quicksort(right)\n \n #combine\n return left + [pivot] + right \n\nelems = sorted(range(20), key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint 'sorted list', quicksort(elems)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\nsorted list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n"
}
],
"prompt_number": 29
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###8 - Quick sort in-place ###\n\nIt will be more messy, but we can also do quick sort in place. Let's first figure out an algorithm for in-place partitioning. This will need to take a list, a left bound (a) and a right bound (b), select a pivot and put everything less than the pivot to the left, and everything greater than the pivot to the right."
},
{
"cell_type": "code",
"collapsed": false,
"input": "#see slides\ndef in_place_partition(iterable, a, b):\n #you can use whatever scheme for picking the pivot\n pivot = iterable[a]\n #bounds on our processing\n left = a\n right = b\n \n #quit once we have crossover\n while left < right:\n \n #starting at the left, advance toward the pivot until we find a violator\n while iterable[left] <= pivot:\n left += 1\n #starting at the right, advance toward the pivot until we find a violator\n while iterable[right] > pivot:\n right -= 1\n #we found violators on both sides - swap them.\n if (left < right):\n iterable[left],iterable[right] = iterable[right],iterable[left]\n \n #put the pivot in the middle of the list\n iterable[a] = iterable[right]\n iterable[right] = pivot\n return iterable, right\n\nin_place_partition([5,6,2,1,5,1,4],0,6)",
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 19,
"text": "([1, 4, 2, 1, 5, 5, 6], 5)"
}
],
"prompt_number": 19
},
{
"cell_type": "markdown",
"metadata": {},
"source": "That was the hard part; now the quicksort algorithm is pretty easy."
},
{
"cell_type": "code",
"collapsed": false,
"input": "def inplace_quicksort(iterable, a=None,b=None):\n #a is the start and b is the end.\n a = 0 if a is None else a\n b = len(iterable) if b is None else b\n \n #base case: b=a+1, or a list of 1 element\n if b<=a+1:\n return iterable\n \n #recursive step: partition the list (in place)\n iterable, pivotInd = in_place_partition(iterable, a, b-1) #inclusive\n \n #sort halves in place\n iterable = inplace_quicksort(iterable, a, pivotInd)\n iterable = inplace_quicksort(iterable, pivotInd+1, b)\n return iterable\n\nelems = sorted(range(20), key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint 'sorted list', inplace_quicksort(elems)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\nsorted list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n"
}
],
"prompt_number": 34
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###9 - Rank finding###\n\nThe idea here is given an unsorted list, we want to find the k-th smallest element. The algorithm we use is a blend of indexed BST searching with quicksort. \n\nBase Case: list has one element - return it\n\nRecursive step: Pick a pivot, put everything smaller than the pivot to the left, and everything larger to the right. If the index of the pivot is equal to K, then we're done (return the pivot). If it's less, call kth_smallest(left,k). If it's greater, call kth_smallest(right, k=k-len(left)-1). Thus on average we can eliminate half the list at each step and solve this in O(n).\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "def kth_smallest(iterable, k):\n N = len(iterable)\n #base case: list has one element\n if N == 1:\n return iterable\n \n #recursive step - choose a pivot, make left and right lists\n \n pivot_ind, pivot = N/2, iterable[N/2]\n left = [item for i,item in enumerate(iterable) if item <= pivot and i!=pivot_ind]\n if k == len(left):\n #the kth largest is the pivot!\n return pivot\n if k < len(left):\n return kth_smallest(left, k)\n if k > len(left):\n right = [item for i,item in enumerate(iterable) if item > pivot and i!=pivot_ind] \n return kth_smallest(right, k-len(left)-1)\n \nelems = sorted(range(20), key= lambda x: hash(str(x)))\nprint 'list:', elems\nprint '1st smallest (index 1 if it were sorted)', kth_smallest(elems,k=1)\nprint 'median:', kth_smallest(elems, k=len(elems)/2)\nprint 'using O(nlogn) algorithm:', quicksort(elems)[len(elems)/2]",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "list: [3, 4, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 5, 6, 7, 8, 9, 0, 1, 2]\n1st smallest (index 1 if it were sorted) 1\nmedian: 10\nusing O(nlogn) algorithm: 10\n"
}
],
"prompt_number": 31
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###10 - Closest pair of points###\n\nThe goal here is: given a set of points in the plane, find the pair which are closest. Let's generate some points:\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "xvals = uniform(-1,1,(10))\nyvals = uniform(-1,1,(10))\npairs = [(x,y) for x,y in zip(xvals,yvals)]\nscatter(xvals, yvals)",
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 35,
"text": "<matplotlib.collections.PathCollection at 0x8700358>"
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\nGjdMrwAAAABJRU5ErkJggg==\n",
"text": "<matplotlib.figure.Figure at 0x62c0438>"
}
],
"prompt_number": 35
},
{
"cell_type": "markdown",
"metadata": {},
"source": "And here's the algorithm:\n\nBase case: the plane has 2 points - those are the closest pair.\n\nRecursive step: sort all the pairs by x-coordinate. Split the plane in half, solve each half. If $d$ is the minimum distance for both subproblems, then look at points within $d$ of the split line. In this set of points, find the smallest pair where one lies in the left and the other lies in the right. Return the minimum of $d$ with the distance of the pair we just found.\n\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "def dist(point1, point2):\n return (point1[0]-point2[0])**2 + (point1[1]-point2[1])**2\n\ndef closest_pair(points, sort=True):\n \"\"\"\n Finds the closest pair of points using divide and conquer.\n Returns: point1, point2, distance\n \"\"\"\n #base case\n if len(points) == 2:\n return points[0],points[1],dist(points[0],points[1])\n elif len(points) == 1:\n return points[0],(0,0), sys.maxint\n elif len(points) == 0:\n return (0,0),(0,0),sys.maxint\n \n #sort (once) by x-coord\n if sort:\n points = sorted(points, key= lambda x: x[0])\n sort=False\n \n #divide into subproblems\n N = len(points)\n leftpoints = points[:N/2]\n rightpoints = points[N/2:]\n \n left_p1,left_p2,left_d = closest_pair(leftpoints, False)\n right_p1, right_p2,right_d = closest_pair(rightpoints, False)\n \n #compute minimum distance of the two\n if left_d <= right_d:\n d = left_d\n p1,p2 = left_p1, left_p2\n else:\n d = right_d\n p1,p2 = right_p1, right_p2\n \n #grab points within d of the split\n #**could be done more efficiently**\n leftmidpoints = [point for point in leftpoints if dist(point, points[N/2]) < d]\n rightmidpoints = [point for point in rightpoints if dist(point, points[N/2]) < d]\n \n #get pairs where one is in each half\n for lcandidate in leftmidpoints:\n for i,rcandidate in enumerate(rightmidpoints):\n if i == 6: break #max number of evals needed\n di = dist(lcandidate, rcandidate)\n if di <= d:\n d = di\n p1,p2 = lcandidate, rcandidate\n \n return p1, p2, d\n \np1,p2,dist = closest_pair(pairs)\nscatter(xvals, yvals, color='b')\nscatter([p1[0],p2[0]],[p1[1],p2[1]], color='r')\nshow()\nprint 'dist between red points:', dist",
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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\nSUVORK5CYII=\n",
"text": "<matplotlib.figure.Figure at 0x8822668>"
},
{
"output_type": "stream",
"stream": "stdout",
"text": "dist between red points: 0.0130980124457\n"
}
],
"prompt_number": 41
},
{
"cell_type": "markdown",
"metadata": {},
"source": "###11 - Greedy Knapsack Problem###\n\nThere's a nice dynamic programming solution to this problem, but we didn't get there. This solution is greedy on profit density, $\\frac{p_i}{w_i}$, which is just profit normalized by weight.\n\nGiven a value for K, which is the initial subset size, the algorithm is:\n\nFor all possible subsets of size <= K,\n\n fill knapsack by being greedy on profit density\n \n if solution is better than previous best, update best.\n \nreturn best\n\n\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "import itertools as it\ndef greedy_profit_density(capacity, profits, weights, K):\n \n assert len(profits) == len(weights), 'profits and weights must be parallel arrays!'\n \n #pdensity holds pairs of (profit density, item), ordered by profit density ascending\n pdensity = sorted([(p/w,i) for p,w,i in zip(profits,weights,range(len(weights)))],\n key = lambda x: x[0])\n bestprofit = 0\n bestknapsack = []\n \n for i in range(1,K+1): #for i = 0 up to K (inclusive)\n for initialset in it.combinations(range(len(profits)),i):\n knapsack = list(initialset)\n\n #if we're overpacked, then this is a bad combination\n currentcapacity = sum(weights[item] for item in initialset)\n if currentcapacity > capacity:\n break\n \n candidates = [pair for pair in pdensity if pair[1] not in initialset]\n currentitem = -1 #start at best profit density\n \n #pack to capacity using a greedy method on profit density\n while True:\n nextitem = pdensity[currentitem][1]\n if nextitem in knapsack:\n currentitem -= 1\n continue\n \n if currentcapacity + weights[nextitem] <= capacity:\n #we can fit the next item. pack her up\n knapsack.append(pdensity[currentitem][1])\n currentcapacity += weights[pdensity[currentitem][1]]\n currentitem -= 1\n else:\n #done packing\n break\n \n currentprofit = sum(profits[item] for item in knapsack)\n if currentprofit > bestprofit:\n bestprofit = currentprofit\n bestknapsack = knapsack\n \n return bestknapsack, bestprofit\n \nprofits = [2.,10.]\nweights = [3.,6.]\ncapacity= 7\nprint greedy_profit_density(capacity, profits, weights, K=1)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "([1], 10.0)\n"
}
],
"prompt_number": 56
},
{
"cell_type": "code",
"collapsed": false,
"input": "profits = [10.,8.,6.]\nweights = [7.,3.,2.]\ncapacity= 7\nprint greedy_profit_density(capacity, profits, weights, K=1)",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "([1, 2], 14.0)\n([1, 2], 14.0)\n"
}
],
"prompt_number": 58
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Well, that was fun."
},
{
"cell_type": "markdown",
"metadata": {},
"source": "# Old material #\n\nLet's do some old material in case it appears on the exam.\n\n### LZW compression ###\n\nIn LZW compression, we take advantage of the fact that text (data) usually doesn't have maximum entropy. In other words, it's not totally random and there are repeated parts. To exploit this, we build up a code table based on the text in such a way that it can be reconstructed after transmission without sending the code table. (Lossless compression).\n\nLet's try implementing it, limiting the messages to lowercase letters, and compressing it as integers."
},
{
"cell_type": "code",
"collapsed": false,
"input": "#alphabet = 'abcdefghijklmnopqrstuvwxyz'\nalphabet = 'ab'\n\ndef encode_LZW(string):\n #start each letter with an integer\n table = {letter:i for i,letter in enumerate(alphabet)}\n nextint = len(alphabet)\n \n encoded = []\n start = 0\n #scan from left to right\n while start < len(string):\n \n #find longest prefix which is in the table\n for end in range(start+1, len(string)+1):\n\n if end==len(string):\n #encode what's left\n encoded.append( table[ string[start:end] ] )\n return encoded\n \n #if it's not in the table, add it and encode the previous prefix\n if string[start:end] not in table:\n \n #add current prefix to table\n table[ string[start:end] ] = nextint\n nextint += 1\n \n #encode previous prefix\n encoded.append( table[ string[start:end-1] ] )\n start = end-1\n break\n \n return encoded\n\nprint encode_LZW('abababbabaabbabbaabba')",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "[0, 1, 2, 2, 3, 3, 5, 8, 8]\n"
}
],
"prompt_number": 80
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Now we just need to write a decompressor!\n"
},
{
"cell_type": "code",
"collapsed": false,
"input": "def decode_LZW(encoded):\n #start with our initial table, but reversed\n table = {i:letter for i,letter in enumerate(alphabet)}\n nextint = len(alphabet)\n decoded = []\n start = 0\n\n #scan from left to right\n for start in range(len(string)):\n #read value\n current = string[start]\n \n #decode\n decoded.append( current )\n \n #add to dictionary the string just output and the \n #first char of the next string\n if start != len(string)-1:\n \n #first char of next\n first_ch = table[ start + 1 ][0]\n \n #add to dict\n table[ nextint ] = current + first_ch\n nextint += 1\n \n return ''.join(decoded)\n\nstring = 'abababbabaabbabbaabba'\nprint 'encoded as: ', encode_LZW( string )\nprint 'when decoded:', decode_LZW( encode_LZW( string) )\nassert decode_LZW( encode_LZW( string ) ) == string, 'error'\nprint 'theyre equal!'",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "encoded as: [0, 1, 2, 2, 3, 3, 5, 8, 8]\nwhen decoded: abababbabaabbabbaabba\ntheyre equal!\n"
}
],
"prompt_number": 95
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Since I went through all that effort to write LZW, let's encode some things!"
},
{
"cell_type": "code",
"collapsed": false,
"input": "string = \"What the fuck did you just fucking say about me, you little bitch? I\u2019ll have you know I graduated top of my class in the Navy Seals, and I\u2019ve been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I\u2019m the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You\u2019re fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that\u2019s just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little \u201cclever\u201d comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn\u2019t, you didn\u2019t, and now you\u2019re paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You\u2019re fucking dead, kiddo\"\nstring = string.lower()\nalphabet = set(char for char in string)\nencoded = encode_LZW(string)\nprint 'the original string was:', len(string), 'chars long, the compressed was:', len(encoded), 'ints long.\\n\\n'\n\nprint 'decoded:', decode_LZW(encoded), '\\n\\n'\nprint 'encoded:', ''.join(str(i) for i in encode_LZW(string))",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "the original string was: 1537 chars long, the compressed was: 752 ints long.\n\n\ndecoded: "
},
{
"output_type": "stream",
"stream": "stdout",
"text": "what the fuck did you just fucking say about me, you little bitch? i\u2019ll have you know i graduated top of my class in the navy seals, and i\u2019ve been involved in numerous secret raids on al-quaeda, and i have over 300 confirmed kills. i am trained in gorilla warfare and i\u2019m the top sniper in the entire us armed forces. you are nothing to me but just another target. i will wipe you the fuck out with precision the likes of which has never been seen before on this earth, mark my fucking words. you think you can get away with saying that shit to me over the internet? think again, fucker. as we speak i am contacting my secret network of spies across the usa and your ip is being traced right now so you better prepare for the storm, maggot. the storm that wipes out the pathetic little thing you call your life. you\u2019re fucking dead, kid. i can be anywhere, anytime, and i can kill you in over seven hundred ways, and that\u2019s just with my bare hands. not only am i extensively trained in unarmed combat, but i have access to the entire arsenal of the united states marine corps and i will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. if only you could have known what unholy retribution your little \u201cclever\u201d comment was about to bring down upon you, maybe you would have held your fucking tongue. but you couldn\u2019t, you didn\u2019t, and now you\u2019re paying the price, you goddamn idiot. i will shit fury all over you and you will drown in it. you\u2019re fucking dead, kiddo \n\n\nencoded: 3320123243220154183113214161916435253142231294046481926174291235412145640231565456424193232244414841320104191101242443834445557212625339441730121631391553322528425184237113241229299426414342612347129151224297912265395971034141515136653425241031521362631773056134144133015401145213425136146527117119121491511111024425103304988413182181930775321199929711112234117926119135112253021113143312301823044150152961217424412112329261928151976513744159322064460722072211822413152138057233139254265671211278831405961721082401973223017177214192282262912472662514547497529142428176131929191822968321264124126371991101132139151961561581361441593252611761241362711341453042792323049128297646622822416265105137654935213150112177722297133842068702721373968208441252751243242401353224822032932712649121712657963313302143213344292471249290216202182285132543473441741764038433224299126402193141217525133296601228153105249123307325369321142635322517204171096837635215785248303176282683312966122423340390172702143804622073704036424718127729645824025413828486241373347356146100435484181521410523733239536916145167921016289202358157281353724878513254771901525101362109929525019545514410313620311511765336214843347323713427947436512771142683815126528233324714072901536382263081035572183922201664535230208408235503279144782901937213263429274380253255258144140100125430245382536111813434122522040830281342351113645266044374274841344652656156340274475104561972330724873877242912512638023226331529620326412253793528385636293733228912618256735656245358410710913637371535201637141622558125431049365248649631303232489625767764722832173300274146066716668572286805646535512352419316643201554371549350134712166314961564786627141626237579295517317335212691104989733245835440138304318517352467161621616051468613292655374463055852652919735261271229216171331601606572664994472170250450652703\n"
}
],
"prompt_number": 112
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Fun stuff. "
},
{
"cell_type": "markdown",
"metadata": {},
"source": "### Hash Table ###\n\nThere are two components to a hash table: \n\n1. a function mapping objects to an index\n2. a way to handle collisions\n\nLet's build a hash table for strings. To hash (convert to int) a string, I'll do some bitwise operations based on the ordinal values of each character. Hopefully if it's complicated enough, the values will spread out sorta uniformly."
},
{
"cell_type": "code",
"collapsed": false,
"input": "# def strhash(string):\n# value = 19\n# for ch in string:\n# value = value * 31 * ord(ch) + 1\n# return value / 3\n\nprint strhash('aaple')\nprint strhash('AAPL')\nprint strhash('u wot m8')\nprint strhash('orange')",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "2084275098019360008\n150252173586812\n6052857895987282548361784117\n7895143023514566094618\n"
}
],
"prompt_number": 142
},
{
"cell_type": "markdown",
"metadata": {},
"source": "Seems legit. Now let's make the hash table"
},
{
"cell_type": "code",
"collapsed": false,
"input": "class HashTable(object):\n \n def __init__(self):\n self.items = [ [] for i in range(10)]\n self.itemcount = 0\n \n def __getitem__(self, obj):\n #hash, modulus, retrieve\n code = self.strhash(obj)\n ind = code % len(self.items)\n \n if len(self.items[ind]) == 0:\n print 'That item is not in the hash table'\n return\n else:\n #we have more than one item in that bucket.\n #go through until we hit ours\n return next(val for (key,val) in self.items[ind] if self.strhash(key) == code)\n \n def double_and_rehash(self):\n olditems = self.items[::]\n newitems = [[] for i in range(2*len(self.items))] #double size\n self.items = newitems\n for bucket in olditems:\n for key,value in bucket:\n self.__setitem__(key, value) #re-add to the new, doubled length, array\n \n def __setitem__(self, key, value):\n #add item to table\n code = self.strhash(key)\n ind = code%len(self.items)\n self.items[ind].append((key,value))\n self.itemcount += 1\n \n #if we are over capacity, then we need to resize the array and rehash everybody\n if self.itemcount > len(self.items) * 3 / 4:\n self.double_and_rehash()\n \n \n def strhash(self, string):\n value = 19\n for ch in string:\n value = value + 2**ord(ch) / 7\n return value / 3",
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 152
},
{
"cell_type": "code",
"collapsed": false,
"input": "h = HashTable()\nh['two leggers'] = 'suck'\nh['selfies'] = 'only on saturday'\nh['$wag'] = 'if you beliebe'\nprint h.items\nprint 'two leggers', h['two leggers']",
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": "[[], [], [], [('selfies', 'only on saturday')], [('$wag', 'if you beliebe')], [], [('two leggers', 'suck')], [], [], []]\ntwo leggers suck\n"
}
],
"prompt_number": 153
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment