diff --git a/docs/data_structures_and_algorithms/nanodegree_outline.md b/docs/data_structures_and_algorithms/nanodegree_outline.md new file mode 100644 index 0000000..2506026 --- /dev/null +++ b/docs/data_structures_and_algorithms/nanodegree_outline.md @@ -0,0 +1,38 @@ +--- +sidebar_label: 'Nanodegree Outline' +sidebar_position: 1 +--- + +# Data Structures and Algorithms Nanodegree + +[Syllabus](https://d20vrrgs8k4bvw.cloudfront.net/documents/en-US/Enterprise+Syllabi/Generic/Udacity+Enterprise+Syllabus+Data+Structures+%26+Algorithms+nd256.pdf) + +[Noted course in Google Drive](https://docs.google.com/document/d/199eoKlppeCCv1WBt3c3A9RntoR3O2xtduDcYYN6mVYk/edit?usp=sharing) + +## Introduction +Start with a warm welcome to the program by refreshing your Python skills and learning about problem solving and efficiency! + +### Project: [Unscramble Computer Science Problems](https://learn.udacity.com/nanodegrees/nd256/parts/41f9d398-7fa4-48e7-96de-88c163e8fba6/lessons/4f281aec-fbff-45f5-9044-ab5caf4cc5b6/concepts/0fd3dae5-41ca-4597-a4a4-d3842129fd65) + +### Source code: [vnk8071/unscramble_computer_science_problems](https://github.com/vnk8071/machine-learning-in-production/tree/main/projects/unscramble_computer_science_problems) + +## Data Structures +Learn about the core data structures used in programming. + +### Project: [Show Me the Data Structures](https://learn.udacity.com/nanodegrees/nd256/parts/cd0328/lessons/2d9a942c-bcc4-4954-9d65-954b5ab3066f/concepts/07869251-2f07-4202-a00a-0d53922e7f26) + +### Source code: [vnk8071/show_me_the_data_structures](https://github.com/vnk8071/machine-learning-in-production/tree/main/projects/show_me_the_data_structures) + +## Basic Algorithms +Learn about the basic algorithms used in programming. + +### Project: [Problems vs. Algorithms](https://learn.udacity.com/nanodegrees/nd256/parts/cd1887/lessons/032713d7-07e0-468f-8393-7b34bf2899e7/concepts/93275178-63ff-4666-88a3-e66e6e29eb2f) + +### Source code: [vnk8071/problems_vs_algorithms](https://github.com/vnk8071/machine-learning-in-production/tree/main/projects/problems_vs_algorithms) + +## Advanced Algorithms +Learn about the advanced algorithms used in programming. + +### Project: [Route Planner](https://learn.udacity.com/nanodegrees/nd256/parts/cd1888/lessons/dc2487e4-aa4a-4301-a0eb-71a99728d78b/concepts/90577506-dfd3-40b7-9696-e1a3369db256) + +### Source code: [vnk8071/route_planner](https://github.com/vnk8071/machine-learning-in-production/tree/main/projects/route_planner) diff --git a/docs/data_structures_and_algorithms/problems_vs_algorithms.md b/docs/data_structures_and_algorithms/problems_vs_algorithms.md new file mode 100644 index 0000000..c02a597 --- /dev/null +++ b/docs/data_structures_and_algorithms/problems_vs_algorithms.md @@ -0,0 +1,76 @@ +--- +sidebar_label: 'Problems vs. Algorithms' +sidebar_position: 4 +--- + +# Problems vs. Algorithms + +## Data Structure Questions + +For this project, you will answer the seven questions laid out in the next sections. The questions cover a variety of topics related to the basic algorithms you've learned in this course. You will write up a clean and efficient answer in Python, as well as a text explanation of the efficiency of your code and your design choices. + +## Problem 1: Square Root of an Integer + +Find the square root of the integer without using any Python library. You have to find the floor value of the square root. + +For example if the given number is 16, then the answer would be 4. + +If the given number is 27, the answer would be 5 because sqrt(5) = 5.196 whose floor value is 5. + +The expected time complexity is O(log(n)) + +Code in `problem_1.py` and explanation in `explanation_problem_1.md` + +## Problem 2: Search in a Rotated Sorted Array + +You are given a sorted array which is rotated at some random pivot point. + +Example: [0, 1, 2, 4, 5, 6, 7] might become [4, 5, 6, 7, 0, 1, 2] + +You are given a target value to search. If found in the array return its index, otherwise return -1. + +You can assume there are no duplicates in the array and your algorithm's runtime complexity must be in the order of O(log n). + +Example: + +Input: nums = [4, 5, 6, 7, 0, 1, 2], target = 0, Output: 4 + +Code in `problem_2.py` and explanation in `explanation_problem_2.md` + +## Problem 3: Rearrange Array Digits + +Rearrange Array Elements so as to form two number such that their sum is maximum. Return these two numbers. You can assume that all array elements are in the range [0, 9]. The number of digits in both the numbers cannot differ by more than 1. You're not allowed to use any sorting function that Python provides and the expected time complexity is O(nlog(n)). + +For example, the input [1, 2, 3, 4, 5] will return 531, 42. + +Code in `problem_3.py` and explanation in `explanation_problem_3.md` + +## Problem 4: Sort 0, 1, 2 + +Given an input array consisting on only 0, 1, and 2, sort the array in a single traversal. You're not allowed to use any sorting function that Python provides. + +Note: O(n) does not necessarily mean single-traversal. For e.g. if you traverse the array twice, that would still be an O(n) solution but it will not count as single traversal. + +Code in `problem_4.py` and explanation in `explanation_problem_4.md` + +## Problem 5: Autocomplete with Tries + +We've learned about Tries earlier in this course, its time to implement one. Implement a trie with insert, search, and startsWith methods. + +Code in `problem_5.py` and explanation in `explanation_problem_5.md` + +## Problem 6: Unsorted Integer Array + +In this problem, we will look for smallest and largest integer from a list of unsorted integers. The code should run in O(n) time. Do not use Python's inbuilt functions to find min and max. + +Code in `problem_6.py` and explanation in `explanation_problem_6.md` + +## Problem 7: Request Routing in a Web Server with a Trie + +For this exercise we are going to implement an HTTPRouter like you would find in a typical web server using the Trie data structure we learned previously. + +There are many different implementations of HTTP Routers such as regular expressions or simple string matching, but the Trie is an excellent and very efficient data structure for this purpose. + +The purpose of an HTTP Router is to take a URL path like "/", "/about", or "/blog/2019-01-15/my-awesome-post" and figure out what content to return. In a dynamic web server, the content will often come from a block of code called a handler. + +Code in `problem_7.py` and explanation in `explanation_problem_7.md` diff --git a/docs/data_structures_and_algorithms/route_planner.md b/docs/data_structures_and_algorithms/route_planner.md new file mode 100644 index 0000000..3ba9810 --- /dev/null +++ b/docs/data_structures_and_algorithms/route_planner.md @@ -0,0 +1,50 @@ +--- +sidebar_label: 'Route Planner' +sidebar_position: 5 +--- + +# Route Planner + +## Introduction + +This is a simple route planner that uses the A* algorithm to find the shortest path between two points on a grid. The grid is represented as a 2D array of 0s and 1s, where 0s represent open cells and 1s represent obstacles. + +## Path Finder + +This Python program implements a path finder that finds the shortest path between two nodes in a graph space. The graph space is represented by nodes and lines, where each node has an id, x and y coordinates, and each line connects two nodes. + +## Classes + +The program consists of several classes: + +1. `GraphNode`: Represents a node in the graph. It has an id, x and y coordinates, and a list of nodes it is connected to. + +2. `PriorityQueue`: A priority queue that stores nodes and their total distances. It allows adding or updating a node, and removing the node with the smallest total distance. + +3. `GraphSpace`: Represents a graph space that contains nodes and lines. It allows finding linked nodes for a given node. + +4. `PathFinder`: Finds the shortest path between two nodes in a graph space. It uses the A* search algorithm to find the shortest path. + +## Methods + +The main methods in the `PathFinder` class are: + +1. `calculate_distance(from_xy, to_xy)`: Calculates the Euclidean distance between two points. + +2. `initialize_goal(goal_id)` and `initialize_origin(origin_id)`: Initialize the goal and origin nodes respectively. + +3. `add_to_frontier(from_node, to_node)`: Adds a node to the frontier, or updates its total distance if it is already in the frontier. + +4. `get_route_ids()`: Returns the ids of the nodes in the shortest path. + +5. `find_shortest_path(origin_id, goal_id)`: Finds the shortest path between two nodes. + +## Usage + +The main function `shortest_path(map_object, start, goal)` takes a map object and the ids of the start and goal nodes, and returns the shortest path between the start and goal nodes. The map object should have `intersections` and `roads` attributes, where `intersections` is a dictionary of node ids to (x, y) coordinates, and `roads` is a list of lists, where each sublist contains the ids of nodes that a node is connected to. + +## Time and Space Complexity + +The time complexity of the `find_shortest_path` method is O(n log n), where n is the number of nodes in the graph. This is because it uses a priority queue to keep track of the nodes to visit next, and each insertion operation in a priority queue takes log n time. + +The space complexity is O(n), where n is the number of nodes in the graph. This is because it needs to store all nodes in the graph space, and in the worst case, all nodes could be added to the priority queue. diff --git a/docs/data_structures_and_algorithms/show_me_the_data_structures.md b/docs/data_structures_and_algorithms/show_me_the_data_structures.md new file mode 100644 index 0000000..247903c --- /dev/null +++ b/docs/data_structures_and_algorithms/show_me_the_data_structures.md @@ -0,0 +1,51 @@ +--- +sidebar_label: 'Show Me the Data Structures' +sidebar_position: 3 +--- + +# Show me the data structures + +## Data Structure Questions + +For this project, you will answer the six questions laid out in the next sections. The questions cover a variety of topics related to the data structures you've learned in this course. You will write up a clean and efficient answer in Python, as well as a text explanation of the efficiency of your code and your design choices. + +## Problem 1: Least Recently Used Cache + +A Least Recently Used (LRU) Cache is a type of cache in which we remove the least recently used entry when the cache memory reaches its limit. For the current problem, consider both get and set operations as an use operation. + +Your job is to use an appropriate data structure to implement a LRU cache which has the following methods: + +- `set(key, value)`: set the value if the key is not present in the cache. If the cache is at capacity, remove the oldest entry. +- `get(key)`: return the value if the key is present in the cache, otherwise return -1. + +Code in `problem_1.py` and explanation in `explanation_problem_1.md` + +## Problem 2: File Recursion + +For this problem, the goal is to write code for finding all files under a directory (and all directories beneath it) that end with ".c" + +Code in `problem_2.py` and explanation in `explanation_problem_2.md` + +## Problem 3: Huffman Coding + +Huffman coding is a compression algorithm that can be used to compress lists of characters. The algorithm uses a binary tree to assign variable-length codes to each character. The more frequent the character, the shorter the code used to represent it. The binary tree is constructed in such a way that the code assigned to each character is the prefix of any other code assigned to any other character. + +Code in `problem_3.py` and explanation in `explanation_problem_3.md` + +## Problem 4: Active Directory + +In Windows Active Directory, a group can consist of user(s) and group(s) themselves. We can construct this hierarchy as such. Where User is represented by str representing their ids. + +Code in `problem_4.py` and explanation in `explanation_problem_4.md` + +## Problem 5: Blockchain + +A Blockchain is a sequential chain of records, similar to a linked list. Each block contains some information and how it is connected related to the other blocks in the chain. Each block contains a cryptographic hash of the previous block, a timestamp, and transaction data. For our blockchain, we will be using a SHA-256 hash, the Greenwich Mean Time when the block was created, and text strings as the data. + +Code in `problem_5.py` and explanation in `explanation_problem_5.md` + +## Problem 6: Union and Intersection of Two Linked Lists + +Your task for this problem is to fill out the union and intersection functions. The union of two sets A and B is the set of elements which are in A, in B, or in both A and B. The intersection of two sets A and B, denoted by A ∩ B, is the set of all objects that are members of both the sets A and B. + +Code in `problem_6.py` and explanation in `explanation_problem_6.md` diff --git a/docs/data_structures_and_algorithms/unscramble_computer_science_problems.md b/docs/data_structures_and_algorithms/unscramble_computer_science_problems.md new file mode 100644 index 0000000..94fa95c --- /dev/null +++ b/docs/data_structures_and_algorithms/unscramble_computer_science_problems.md @@ -0,0 +1,206 @@ +--- +sidebar_label: 'Unscramble Computer Science Problems' +sidebar_position: 2 +--- + +# Investigating Texts and Calls + +## Project Overview + +In this project, you will complete five tasks based on a fabricated set of calls and texts exchanged during September 2016. You will use Python to analyze and answer questions about the texts and calls contained in the dataset. Lastly, you will perform run time analysis of your solution and determine its efficiency. + +## About the data + +The text and call data are provided in csv files. +The text data `(text.csv)` has the following columns: sending telephone number (string), receiving telephone number (string), timestamp of text message (string). +The call data `(call.csv)` has the following columns: calling telephone number (string), receiving telephone number (string), start timestamp of telephone call (string), duration of telephone call in seconds (string) + +All telephone numbers are 10 or 11 numerical digits long. Each telephone number starts with a code indicating the location and/or type of the telephone number. There are three different kinds of telephone numbers, each with a different format: + +- Fixed lines start with an area code enclosed in brackets. The area codes vary in length but always begin with 0. Example: "(022)40840621". +- Mobile numbers have no parentheses, but have a space in the middle of the number to help readability. The mobile code of a mobile number is its first four digits and they always start with 7, 8 or 9. Example: "93412 66159". +- Telemarketers' numbers have no parentheses or space, but start with the code 140. Example: "1402316533". + +## Task 0 + +What is the first record of texts and what is the last record of calls? + +>Print messages: +"First record of texts, `incoming number` texts `answering number` at time `time>`" +"Last record of calls, `incoming number` calls `answering number` at time `time`, lasting `during` seconds" + +```python +python Task0.py +``` + +Output: + +```python +First record of texts, 97424 22395 texts 90365 06212 at time 01-09-2016 06:03:22 +Last record of calls, 98447 62998 calls (080)46304537 at time 30-09-2016 23:57:15, lasting 2151 seconds +``` + +## Task 1 + +How many different telephone numbers are there in the records? + +>Print a message: +"There are `count` different telephone numbers in the records." + +```python +python Task1.py +``` + +Output: + +```python +There are 570 different telephone numbers in the records. +``` + +## Task 2 + +Which telephone number spent the longest time on the phone during the period? Don't forget that time spent answering a call is also time spent on the phone. + +>Print a message: +"`telephone number` spent the longest time, `total time` seconds, on the phone during September 2016.". + +```python +python Task2.py +``` + +Output: + +```python +(080)33251027 spent the longest time, 90456 seconds, on the phone during September 2016. +``` + +## Task 3 + +(080) is the area code for fixed line telephones in Bangalore. Fixed line numbers include parentheses, so Bangalore numbers have the form (080)xxxxxxx. + +Part A: Find all of the area codes and mobile prefixes called by people in Bangalore. In other words, the calls were initiated by "(080)" area code to the following area codes and mobile prefixes: + +- Fixed lines start with an area code enclosed in brackets. The area codes vary in length but always begin with 0. +- Mobile numbers have no parentheses, but have a space in the middle of the number to help readability. The prefix of a mobile number is its first four digits, and they always start with 7, 8 or 9. +- Telemarketers' numbers have no parentheses or space, but they start with the area code 140. + +>Print the answer as part of a message: +"The numbers called by people in Bangalore have codes:" + `list of codes` +The list of codes should be print out one per line in lexicographic order with no duplicates. + +Part B: What percentage of calls from fixed lines in Bangalore are made to fixed lines also in Bangalore? In other words, of all the calls made from a number starting with "(080)", what percentage of these calls were made to a number also starting with "(080)"? + +>Print the answer as a part of a message:: +"`percentage` percent of calls from fixed lines in Bangalore are calls +to other fixed lines in Bangalore." +The percentage should have 2 decimal digits + +```python +Part A: +The numbers called by people in Bangalore have codes: +(022) +(040) +(04344) +(044) +(04546) +(0471) +(080) +(0821) +7406 +7795 +7813 +7829 +8151 +8152 +8301 +8431 +8714 +9008 +9019 +9035 +9036 +9241 +9242 +9341 +9342 +9343 +9400 +9448 +9449 +9526 +9656 +9738 +9740 +9741 +9742 +9844 +9845 +9900 +9961 +---------------------------------------- +Part B: +24.81 percent of calls from fixed lines in Bangalore are calls to other fixed lines in Bangalore. +``` + +## Task 4 + +The telephone company want to identify numbers that might be doing telephone marketing. Create a set of possible telemarketers: these are numbers that make outgoing calls but never send texts, receive texts or receive incoming calls. + +>Print a message: +"These numbers could be telemarketers: " +`list of numbers` +The list of numbers should be print out one per line in lexicographic order with no duplicates. + +```python +python Task4.py +``` + +Output: + +```python +These numbers could be telemarketers: +(022)37572285 +(022)65548497 +(022)68535788 +(022)69042431 +(040)30429041 +(044)22020822 +(0471)2171438 +(0471)6579079 +(080)20383942 +(080)25820765 +(080)31606520 +(080)40362016 +(080)60463379 +(080)60998034 +(080)62963633 +(080)64015211 +(080)69887826 +(0821)3257740 +1400481538 +1401747654 +1402316533 +1403072432 +1403579926 +1404073047 +1404368883 +1404787681 +1407539117 +1408371942 +1408409918 +1408672243 +1409421631 +1409668775 +1409994233 +74064 66270 +78291 94593 +87144 55014 +90351 90193 +92414 69419 +94495 03761 +97404 30456 +97407 84573 +97442 45192 +99617 25274 +``` diff --git a/projects/problems_vs_algorithms/README.md b/projects/problems_vs_algorithms/README.md new file mode 100644 index 0000000..6d92303 --- /dev/null +++ b/projects/problems_vs_algorithms/README.md @@ -0,0 +1,71 @@ +# Problems vs. Algorithms + +## Data Structure Questions + +For this project, you will answer the seven questions laid out in the next sections. The questions cover a variety of topics related to the basic algorithms you've learned in this course. You will write up a clean and efficient answer in Python, as well as a text explanation of the efficiency of your code and your design choices. + +## Problem 1: Square Root of an Integer + +Find the square root of the integer without using any Python library. You have to find the floor value of the square root. + +For example if the given number is 16, then the answer would be 4. + +If the given number is 27, the answer would be 5 because sqrt(5) = 5.196 whose floor value is 5. + +The expected time complexity is O(log(n)) + +Code in `problem_1.py` and explanation in `explanation_problem_1.md` + +## Problem 2: Search in a Rotated Sorted Array + +You are given a sorted array which is rotated at some random pivot point. + +Example: [0, 1, 2, 4, 5, 6, 7] might become [4, 5, 6, 7, 0, 1, 2] + +You are given a target value to search. If found in the array return its index, otherwise return -1. + +You can assume there are no duplicates in the array and your algorithm's runtime complexity must be in the order of O(log n). + +Example: + +Input: nums = [4, 5, 6, 7, 0, 1, 2], target = 0, Output: 4 + +Code in `problem_2.py` and explanation in `explanation_problem_2.md` + +## Problem 3: Rearrange Array Digits + +Rearrange Array Elements so as to form two number such that their sum is maximum. Return these two numbers. You can assume that all array elements are in the range [0, 9]. The number of digits in both the numbers cannot differ by more than 1. You're not allowed to use any sorting function that Python provides and the expected time complexity is O(nlog(n)). + +For example, the input [1, 2, 3, 4, 5] will return 531, 42. + +Code in `problem_3.py` and explanation in `explanation_problem_3.md` + +## Problem 4: Sort 0, 1, 2 + +Given an input array consisting on only 0, 1, and 2, sort the array in a single traversal. You're not allowed to use any sorting function that Python provides. + +Note: O(n) does not necessarily mean single-traversal. For e.g. if you traverse the array twice, that would still be an O(n) solution but it will not count as single traversal. + +Code in `problem_4.py` and explanation in `explanation_problem_4.md` + +## Problem 5: Autocomplete with Tries + +We've learned about Tries earlier in this course, its time to implement one. Implement a trie with insert, search, and startsWith methods. + +Code in `problem_5.py` and explanation in `explanation_problem_5.md` + +## Problem 6: Unsorted Integer Array + +In this problem, we will look for smallest and largest integer from a list of unsorted integers. The code should run in O(n) time. Do not use Python's inbuilt functions to find min and max. + +Code in `problem_6.py` and explanation in `explanation_problem_6.md` + +## Problem 7: Request Routing in a Web Server with a Trie + +For this exercise we are going to implement an HTTPRouter like you would find in a typical web server using the Trie data structure we learned previously. + +There are many different implementations of HTTP Routers such as regular expressions or simple string matching, but the Trie is an excellent and very efficient data structure for this purpose. + +The purpose of an HTTP Router is to take a URL path like "/", "/about", or "/blog/2019-01-15/my-awesome-post" and figure out what content to return. In a dynamic web server, the content will often come from a block of code called a handler. + +Code in `problem_7.py` and explanation in `explanation_problem_7.md` diff --git a/projects/problems_vs_algorithms/explanation_problem1.md b/projects/problems_vs_algorithms/explanation_problem1.md new file mode 100644 index 0000000..2f4b7ed --- /dev/null +++ b/projects/problems_vs_algorithms/explanation_problem1.md @@ -0,0 +1,5 @@ +# Square Root of an Integer + +The sqrt function uses a binary search algorithm to find the square root of a number, which has a time complexity of O(log n), where n is the input number. This is because with each iteration, the binary search algorithm halves the search space. + +The space complexity of the sqrt function is O(1), which means it uses a constant amount of space. This is because the space used does not change with the size of the input number, it only uses a few integer variables to store the intermediate results. diff --git a/projects/problems_vs_algorithms/explanation_problem2.md b/projects/problems_vs_algorithms/explanation_problem2.md new file mode 100644 index 0000000..f6107fa --- /dev/null +++ b/projects/problems_vs_algorithms/explanation_problem2.md @@ -0,0 +1,9 @@ +# Rotated Array Search + +The rotated_array_search function uses a binary search algorithm, which has a time complexity of O(log n), where n is the size of the input list. This is because with each iteration, the binary search algorithm halves the search space. + +The space complexity of the rotated_array_search function is O(1), which means it uses a constant amount of space. This is because the space used does not change with the size of the input list, it only uses a few integer variables to store the intermediate results. + +The linear_search function has a time complexity of O(n), where n is the size of the input list. This is because in the worst-case scenario, it needs to iterate over each element in the list. + +The space complexity of the linear_search function is also O(1), as it only uses a couple of variables and does not depend on the size of the input list. diff --git a/projects/problems_vs_algorithms/explanation_problem3.md b/projects/problems_vs_algorithms/explanation_problem3.md new file mode 100644 index 0000000..5ae2a17 --- /dev/null +++ b/projects/problems_vs_algorithms/explanation_problem3.md @@ -0,0 +1,5 @@ +# Rearrange Array Elements + +The rearrange_digits function has a time complexity of O(n log n), where n is the size of the input list. This is due to the use of the sorted function, which in Python uses a sorting algorithm called Timsort that has a worst-case time complexity of O(n log n). The subsequent for loop has a time complexity of O(n), but since O(n log n) is the dominant term, it's the one we use to express the overall time complexity. + +The space complexity of the rearrange_digits function is O(n), because the sorted function returns a new list that contains all of the elements from the input list. The rest of the function only uses a constant amount of space to store the two output numbers and the loop index and value, so the overall space complexity is determined by the space required for the sorted list. diff --git a/projects/problems_vs_algorithms/explanation_problem4.md b/projects/problems_vs_algorithms/explanation_problem4.md new file mode 100644 index 0000000..ae5f172 --- /dev/null +++ b/projects/problems_vs_algorithms/explanation_problem4.md @@ -0,0 +1,5 @@ +# Sort 0, 1, 2 + +The sort_012 function has a time complexity of O(n), where n is the size of the input list. This is because the function traverses the list once, performing a constant amount of work for each element. + +The space complexity of the sort_012 function is O(1), which means it uses a constant amount of space. This is because the space used does not change with the size of the input list, it only uses a few integer variables to store the intermediate results. diff --git a/projects/problems_vs_algorithms/explanation_problem5.md b/projects/problems_vs_algorithms/explanation_problem5.md new file mode 100644 index 0000000..c5705f4 --- /dev/null +++ b/projects/problems_vs_algorithms/explanation_problem5.md @@ -0,0 +1,29 @@ +# Trie Data Structure + +## The `TrieNode` class has different time and space complexities for different operations + +1. `__init__` operation: + - Time complexity: O(1), as it simply initializes a new node with an empty dictionary and a couple of boolean/None values. + - Space complexity: O(1), as it uses a constant amount of space to store the initialized values. + +2. `insert` operation: + - Time complexity: O(1), as it simply adds a new entry to the dictionary. + - Space complexity: O(1), as it only creates a single new `TrieNode`. + +3. `suffixes` operation: + - Time complexity: O(n), where n is the total number of nodes in the subtree rooted at this node. This is because it needs to traverse all nodes in the subtree to find all suffixes. + - Space complexity: O(m), where m is the total number of complete words in the subtree. This is because it needs to store all complete words in the `suffixes` list. In the worst case, every node in the subtree is a complete word, so m could be as large as n. + +## The `Trie` class has different time and space complexities for different operations + +1. `insert` operation: + - Time complexity: O(m), where m is the length of the word being inserted. This is because we're simply iterating over the word and inserting each character into the trie. + - Space complexity: O(m). In the worst case, if the word doesn't share any prefix with the words already in the trie, we need to add m new nodes to the trie. + +2. `find` operation: + - Time complexity: O(m), where m is the length of the word being searched for. This is because we're simply iterating over the word and traversing the trie. + - Space complexity: O(1). We're not using any additional space that scales with the size of the input. + +3. `suffixes` operation: + - Time complexity: O(n), where n is the total number of characters in all words stored in the trie. This is because in the worst case, we might need to traverse the entire trie to find all suffixes. + - Space complexity: O(n). In the worst case, we might need to store all characters in the trie in the `suffixes` list. diff --git a/projects/problems_vs_algorithms/explanation_problem6.md b/projects/problems_vs_algorithms/explanation_problem6.md new file mode 100644 index 0000000..9e0991f --- /dev/null +++ b/projects/problems_vs_algorithms/explanation_problem6.md @@ -0,0 +1,5 @@ +# Get Min Max + +The get_min_max function has a time complexity of O(n), where n is the size of the input list. This is because the function traverses the list once, checking each value to see if it's the new minimum or maximum. + +The space complexity of the get_min_max function is O(1), which means it uses a constant amount of space. This is because the space used does not change with the size of the input list, it only uses two integer variables to store the minimum and maximum values. diff --git a/projects/problems_vs_algorithms/explanation_problem7.md b/projects/problems_vs_algorithms/explanation_problem7.md new file mode 100644 index 0000000..0a7cbdb --- /dev/null +++ b/projects/problems_vs_algorithms/explanation_problem7.md @@ -0,0 +1,33 @@ +# Router Traversal and Lookup + +## The `RouteTrie` class has different time and space complexities for different operations + +1. `insert` operation: + - Time complexity: O(m), where m is the number of parts in the path. This is because we're simply iterating over the path and inserting each part into the trie. + - Space complexity: O(m). In the worst case, if the path doesn't share any prefix with the paths already in the trie, we need to add m new nodes to the trie. + +2. `find` operation: + - Time complexity: O(m), where m is the number of parts in the path being searched for. This is because we're simply iterating over the path and traversing the trie. + - Space complexity: O(1). We're not using any additional space that scales with the size of the input. + +3. `_split_path` operation: + - Time complexity: O(m), where m is the length of the path string. This is because we need to iterate over the string to split it into parts. + - Space complexity: O(m). The `split` function returns a new list that contains all of the parts of the path, so we need an amount of space proportional to the size of the path to store this list. + +## The `Router` class has different time and space complexities for different operations + +1. `__init__` operation: + - Time complexity: O(1), as it simply initializes a new `RouteTrie`. + - Space complexity: O(1), as it uses a constant amount of space to store the `RouteTrie`. + +2. `add_handler` operation: + - Time complexity: O(m), where m is the number of parts in the path. This is because it needs to split the path and insert it into the `RouteTrie`. + - Space complexity: O(m). In the worst case, if the path doesn't share any prefix with the paths already in the trie, we need to add m new nodes to the trie. + +3. `lookup` operation: + - Time complexity: O(m), where m is the number of parts in the path being searched for. This is because it needs to find the path in the `RouteTrie`. + - Space complexity: O(1). We're not using any additional space that scales with the size of the input. + +4. `split_path` operation: + - Time complexity: O(m), where m is the length of the path string. This is because we need to iterate over the string to split it into parts. + - Space complexity: O(m). The `split` function returns a new list that contains all of the parts of the path, so we need an amount of space proportional to the size of the path to store this list. diff --git a/projects/problems_vs_algorithms/problem1.py b/projects/problems_vs_algorithms/problem1.py new file mode 100644 index 0000000..bfe47a5 --- /dev/null +++ b/projects/problems_vs_algorithms/problem1.py @@ -0,0 +1,46 @@ +def sqrt(number): + """ + Calculate the floored square root of a number + + Args: + number(int): Number to find the floored squared root + Returns: + int: Floored Square Root + """ + if number < 0: + return None + if number == 0 or number == 1: + return number + start = 1 + end = number + while start <= end: + middle = (start + end) // 2 + if middle * middle == number: + return middle + elif middle * middle < number: + start = middle + 1 + result = middle + else: + end = middle - 1 + return result + + +# Test Cases 1 +assert 3 == sqrt(9), "Test Case 1 Failed, expected 3" +assert 4 == sqrt(16), "Test Case 3 Failed, expected 4" + +# Test Cases 2 +assert 0 == sqrt(0), "Test Case 2 Failed, expected 0" +assert 1 == sqrt(1), "Test Case 4 Failed, expected 1" + +# Test Cases 3 +assert 5 == sqrt(27), "Test Case 5 Failed, expected 5" +assert 5 == sqrt(30), "Test Case 6 Failed, expected 5" + +# Edge Cases 1 +assert 100 == sqrt(10003), "Test Case 7 Failed, expected 100" + +# Edge Cases 2 +assert None is sqrt(-1), "Test Case 8 Failed, expected None" + +print("All test cases passed!") diff --git a/projects/problems_vs_algorithms/problem2.py b/projects/problems_vs_algorithms/problem2.py new file mode 100644 index 0000000..ab30a92 --- /dev/null +++ b/projects/problems_vs_algorithms/problem2.py @@ -0,0 +1,88 @@ +def rotated_array_search(input_list, number): + """ + Find the index by searching in a rotated sorted array + + Args: + input_list(array), number(int): Input array to search and the target + + Returns: + int: Index or -1 + """ + + def binary_search(start_index, end_index): + """Binary search in a rotated sorted array + + Args: + start_index(int), end_index(int): Start and end index of the array + + Returns: + int: Index or -1""" + while start_index <= end_index: + middle_index = (start_index + end_index) // 2 + middle_value = input_list[middle_index] + if middle_value == number: + return middle_index + elif input_list[start_index] <= middle_value: + if input_list[start_index] <= number < middle_value: + end_index = middle_index - 1 + else: + start_index = middle_index + 1 + else: + if middle_value < number <= input_list[end_index]: + start_index = middle_index + 1 + else: + end_index = middle_index - 1 + return -1 + + start_index = 0 + end_index = len(input_list) - 1 + + return binary_search(start_index, end_index) + + +def linear_search(input_list, number): + """Linear search in a list + + Args: + input_list(array), number(int): Input array to search and the target + + Returns: + int: Index or -1 + """ + for index, element in enumerate(input_list): + if element == number: + return index + return -1 + + +def test_function(test_case): + input_list = test_case[0] + number = test_case[1] + if linear_search( + input_list, + number) == rotated_array_search( + input_list, + number): + print("Pass") + else: + print("Fail") + + +# Test Cases 1 +test_function([[6, 7, 8, 9, 10, 1, 2, 3, 4], 6]) +test_function([[6, 7, 8, 9, 10, 1, 2, 3, 4], 1]) + +# Test Cases 2 +test_function([[6, 7, 8, 1, 2, 3, 4], 8]) +test_function([[6, 7, 8, 1, 2, 3, 4], 1]) + +# Test Cases 3 +test_function([[6, 7, 8, 1, 2, 3, 4], 10]) + +# Edge Cases 1 +test_function([[], 10]) + +# Edge Cases 2 +test_function([[1], 10]) + +print("All test cases passed!") diff --git a/projects/problems_vs_algorithms/problem3.py b/projects/problems_vs_algorithms/problem3.py new file mode 100644 index 0000000..9346180 --- /dev/null +++ b/projects/problems_vs_algorithms/problem3.py @@ -0,0 +1,47 @@ +def rearrange_digits(input_list): + """ + Rearrange Array Elements so as to form two number such that their sum is maximum. + + Args: + input_list(list): Input List + Returns: + (int),(int): Two maximum sums + """ + if not input_list or len(input_list) < 2: + return [0, 0] + first_number = 0 + second_number = 0 + sorted_list = sorted(input_list, reverse=True) + for index, value in enumerate(sorted_list): + if index % 2 == 0: + first_number = first_number * 10 + value + else: + second_number = second_number * 10 + value + return [first_number, second_number] + + +def test_function(test_case): + output = rearrange_digits(test_case[0]) + solution = test_case[1] + if sum(output) == sum(solution): + print("Pass") + else: + print("Fail") + + +# Test Cases 1 +test_function([[1, 2, 3, 4, 5], [542, 31]]) + +# Test Cases 2 +test_function([[4, 6, 2, 5, 9, 8], [964, 852]]) + +# Test Cases 3 +test_function([[0, 0], [0, 0]]) + +# Edge Cases 1 +test_function([[], [0, 0]]) + +# Edge Cases 2 +test_function([[1], [0, 0]]) + +print("All test cases passed!") diff --git a/projects/problems_vs_algorithms/problem4.py b/projects/problems_vs_algorithms/problem4.py new file mode 100644 index 0000000..2a08614 --- /dev/null +++ b/projects/problems_vs_algorithms/problem4.py @@ -0,0 +1,55 @@ +def sort_012(input_list): + """ + Given an input array consisting on only 0, 1, and 2, sort the array in a single traversal. + + Args: + input_list(list): List to be sorted + """ + current_index = 0 + start_index = 0 + end_index = len(input_list) - 1 + while current_index <= end_index: + if input_list[current_index] == 0: + input_list[start_index], input_list[current_index] = ( + input_list[current_index], + input_list[start_index], + ) + start_index += 1 + current_index += 1 + elif input_list[current_index] == 2: + input_list[end_index], input_list[current_index] = ( + input_list[current_index], + input_list[end_index], + ) + end_index -= 1 + else: + current_index += 1 + return input_list + + +def test_function(test_case): + sorted_array = sort_012(test_case) + if sorted_array == sorted(test_case): + print("Pass") + else: + print("Fail") + + +# Test Cases 1 +test_function([0, 0, 2, 2, 2, 1, 1, 1, 2, 0, 2]) + +# Test Cases 2 +test_function([2, 1, 2, 0, 0, 2, 1, 0, 1, 0, 0, 2, 2, + 2, 1, 2, 0, 0, 0, 2, 1, 0, 2, 0, 0, 1]) + +# Test Cases 3 +test_function([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2]) + +# Edge Cases 1 +test_function([0]) +test_function([0, 1, 2]) + +# Edge Cases 2 +test_function([]) + +print("All test cases passed!") diff --git a/projects/problems_vs_algorithms/problem5.ipynb b/projects/problems_vs_algorithms/problem5.ipynb new file mode 100644 index 0000000..760da0b --- /dev/null +++ b/projects/problems_vs_algorithms/problem5.ipynb @@ -0,0 +1,171 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Building a Trie in Python\n", + "\n", + "Before we start let us reiterate the key components of a Trie or Prefix Tree. A trie is a tree-like data structure that stores a dynamic set of strings. Tries are commonly used to facilitate operations like predictive text or autocomplete features on mobile phones or web search.\n", + "\n", + "Before we move into the autocomplete function we need to create a working trie for storing strings. We will create two classes:\n", + "* A `Trie` class that contains the root node (empty string)\n", + "* A `TrieNode` class that exposes the general functionality of the Trie, like inserting a word or finding the node which represents a prefix.\n", + "\n", + "Give it a try by implementing the `TrieNode` and `Trie` classes below!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Finding Suffixes\n", + "\n", + "Now that we have a functioning Trie, we need to add the ability to list suffixes to implement our autocomplete feature. To do that, we need to implement a new function on the `TrieNode` object that will return all complete word suffixes that exist below it in the trie. For example, if our Trie contains the words `[\"fun\", \"function\", \"factory\"]` and we ask for suffixes from the `f` node, we would expect to receive `[\"un\", \"unction\", \"actory\"]` back from `node.suffixes()`.\n", + "\n", + "Using the code you wrote for the `TrieNode` above, try to add the suffixes function below. (Hint: recurse down the trie, collecting suffixes as you go.)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "class TrieNode:\n", + " def __init__(self):\n", + " ## Initialize this node in the Trie\n", + " self.children = {}\n", + " self.is_word = False\n", + " self.word = None\n", + " \n", + " def insert(self, char):\n", + " ## Add a child node in this Trie\n", + " self.children[char] = TrieNode()\n", + "\n", + " \n", + " def suffixes(self, suffix = ''):\n", + " ## Recursive function that collects the suffix for \n", + " ## all complete words below this point\n", + " suffixes = []\n", + " for char, node in self.children.items():\n", + " if node.is_word:\n", + " suffixes.append(suffix + char)\n", + " suffixes.extend(node.suffixes(suffix + char))\n", + " return suffixes\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "class Trie:\n", + " def __init__(self):\n", + " self.root = TrieNode()\n", + " \n", + " def insert(self, word):\n", + " current_node = self.root\n", + " for char in word:\n", + " if char not in current_node.children:\n", + " current_node.insert(char)\n", + " current_node = current_node.children[char]\n", + " current_node.is_word = True\n", + " current_node.word = word\n", + " \n", + " def find(self, word):\n", + " current_node = self.root\n", + " for char in word:\n", + " if char not in current_node.children:\n", + " return False\n", + " current_node = current_node.children[char]\n", + " return current_node\n", + " \n", + " def suffixes(self, prefix):\n", + " self.root.suffixes(prefix)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "MyTrie = Trie()\n", + "wordList = [\n", + " \"ant\", \"anthology\", \"antagonist\", \"antonym\", \n", + " \"fun\", \"function\", \"factory\", \n", + " \"trie\", \"trigger\", \"trigonometry\", \"tripod\"\n", + "]\n", + "for word in wordList:\n", + " MyTrie.insert(word)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "20607424c5014838998f513c47c6e0e0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Text(value='', description='prefix'), Output()), _dom_classes=('widget-interact',))" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from ipywidgets import widgets\n", + "from IPython.display import display\n", + "from ipywidgets import interact\n", + "\n", + "def f(prefix):\n", + " if prefix != '':\n", + " prefixNode = MyTrie.find(prefix)\n", + " if prefixNode:\n", + " print('\\n'.join(prefixNode.suffixes()))\n", + " else:\n", + " print(prefix + \" not found\")\n", + " else:\n", + " print('')\n", + "interact(f,prefix='');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ds1", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/projects/problems_vs_algorithms/problem6.py b/projects/problems_vs_algorithms/problem6.py new file mode 100644 index 0000000..5731f3b --- /dev/null +++ b/projects/problems_vs_algorithms/problem6.py @@ -0,0 +1,43 @@ +import random + + +def get_min_max(ints): + """ + Return a tuple(min, max) out of list of unsorted integers. + + Args: + ints(list): list of integers containing one or more integers + """ + if not ints or len(ints) < 2: + return None + min_value = ints[0] + max_value = ints[0] + for value in ints: + if value < min_value: + min_value = value + if value > max_value: + max_value = value + return min_value, max_value + + +# Example Test Case of Ten Integers + +l = [i for i in range(0, 10)] # a list containing 0 - 9 +random.shuffle(l) + +# Test Cases 1 +print("Pass" if ((0, 9) == get_min_max(l)) else "Fail") + +# Test Cases 2 +print("Pass" if ((0, 0) == get_min_max([0, 0])) else "Fail") + +# Test Cases 3 +print("Pass" if ((3, 7) == get_min_max([3, 7])) else "Fail") + +# Edge Cases 1 +print("Pass" if (None is get_min_max([])) else "Fail") + +# Edge Cases 2 +print("Pass" if (None is get_min_max(None)) else "Fail") + +print("All test cases passed!") diff --git a/projects/problems_vs_algorithms/problem7.py b/projects/problems_vs_algorithms/problem7.py new file mode 100644 index 0000000..f06a515 --- /dev/null +++ b/projects/problems_vs_algorithms/problem7.py @@ -0,0 +1,103 @@ +# A RouteTrie will store our routes and their associated handlers +class RouteTrie: + def __init__(self, root_handler, invalid_handler): + # Initialize the trie with an root node and a handler, this is the root + # path or home page node + self.root = RouteTrieNode(root_handler) + self.invalid_handler = invalid_handler + + def insert(self, path, handler): + # Similar to our previous example you will want to recursively add nodes + # Make sure you assign the handler to only the leaf (deepest) node of + # this path + current_node = self.root + for part in path: + if part not in current_node.children: + current_node.children[part] = RouteTrieNode() + current_node = current_node.children[part] + current_node.handler = handler + + def find(self, path): + # Starting at the root, navigate the Trie to find a match for this path + # Return the handler for a match, or None for no match + path_parts = self._split_path(path) + current_node = self.root + for part in path_parts: + if part not in current_node.children: + return self.invalid_handler + current_node = current_node.children[part] + return current_node.handler + + def _split_path(self, path): + # you need to split the path into parts for + # both the add_handler and loopup functions, + # so it should be placed in a function here + path = path.strip("/") + return path.split("/") if path else [] + + +# A RouteTrieNode will be similar to our autocomplete TrieNode... with one +# additional element, a handler. +class RouteTrieNode: + def __init__(self, handler=None): + # Initialize the node with children as before, plus a handler + self.children = {} + self.handler = handler + + +# The Router class will wrap the Trie and handle +class Router: + def __init__(self, root_handler, invalid_handler): + # Create a new RouteTrie for holding our routes + # You could also add a handler for 404 page not found responses as + # well! + self.root = RouteTrie(root_handler, invalid_handler) + + def add_handler(self, path, handler): + # Add a handler for a path + # You will need to split the path and pass the pass parts + # as a list to the RouteTrie + self.root.insert(self.split_path(path), handler) + + def lookup(self, path): + # lookup path (by parts) and return the associated handler + # you can return None if it's not found or + # return the "not found" handler if you added one + # bonus points if a path works with and without a trailing slash + # e.g. /about and /about/ both return the /about handler + return self.root.find(path) + + def split_path(self, path): + # you need to split the path into parts for + # both the add_handler and loopup functions, + # so it should be placed in a function here + return self.root._split_path(path) + + +# create the router and add a route +router = Router( + root_handler="root handler", invalid_handler="not found handler" +) # remove the 'not found handler' if you did not implement this +router.add_handler(path="/home/about", handler="about handler") # add a route + +# Test Cases 1 +print(router.lookup("/")) # should print 'root handler' + +# Test Cases 2 +print( + router.lookup("/home") +) # should print 'not found handler' or None if you did not implement one + +# Test Cases 3 +print(router.lookup("/home/about")) # should print 'about handler' + +# Edge Cases 1 +# should print 'about handler' or None if you did not handle trailing slashes +print(router.lookup("/home/about/")) + +# Edge Cases 2 +print( + router.lookup("/home/about/me") +) # should print 'not found handler' or None if you did not implement one + +print("All test cases passed!") diff --git a/projects/route_planner/README.md b/projects/route_planner/README.md new file mode 100644 index 0000000..5b1a642 --- /dev/null +++ b/projects/route_planner/README.md @@ -0,0 +1,45 @@ +# Route Planner + +## Introduction + +This is a simple route planner that uses the A* algorithm to find the shortest path between two points on a grid. The grid is represented as a 2D array of 0s and 1s, where 0s represent open cells and 1s represent obstacles. + +## Path Finder + +This Python program implements a path finder that finds the shortest path between two nodes in a graph space. The graph space is represented by nodes and lines, where each node has an id, x and y coordinates, and each line connects two nodes. + +## Classes + +The program consists of several classes: + +1. `GraphNode`: Represents a node in the graph. It has an id, x and y coordinates, and a list of nodes it is connected to. + +2. `PriorityQueue`: A priority queue that stores nodes and their total distances. It allows adding or updating a node, and removing the node with the smallest total distance. + +3. `GraphSpace`: Represents a graph space that contains nodes and lines. It allows finding linked nodes for a given node. + +4. `PathFinder`: Finds the shortest path between two nodes in a graph space. It uses the A* search algorithm to find the shortest path. + +## Methods + +The main methods in the `PathFinder` class are: + +1. `calculate_distance(from_xy, to_xy)`: Calculates the Euclidean distance between two points. + +2. `initialize_goal(goal_id)` and `initialize_origin(origin_id)`: Initialize the goal and origin nodes respectively. + +3. `add_to_frontier(from_node, to_node)`: Adds a node to the frontier, or updates its total distance if it is already in the frontier. + +4. `get_route_ids()`: Returns the ids of the nodes in the shortest path. + +5. `find_shortest_path(origin_id, goal_id)`: Finds the shortest path between two nodes. + +## Usage + +The main function `shortest_path(map_object, start, goal)` takes a map object and the ids of the start and goal nodes, and returns the shortest path between the start and goal nodes. The map object should have `intersections` and `roads` attributes, where `intersections` is a dictionary of node ids to (x, y) coordinates, and `roads` is a list of lists, where each sublist contains the ids of nodes that a node is connected to. + +## Time and Space Complexity + +The time complexity of the `find_shortest_path` method is O(n log n), where n is the number of nodes in the graph. This is because it uses a priority queue to keep track of the nodes to visit next, and each insertion operation in a priority queue takes log n time. + +The space complexity is O(n), where n is the number of nodes in the graph. This is because it needs to store all nodes in the graph space, and in the worst case, all nodes could be added to the priority queue. diff --git a/projects/route_planner/helpers.py b/projects/route_planner/helpers.py new file mode 100644 index 0000000..8d6996d --- /dev/null +++ b/projects/route_planner/helpers.py @@ -0,0 +1,102 @@ +import pickle +import random + +import networkx as nx +from plotly.graph_objs import * +from plotly.offline import init_notebook_mode, iplot, plot + +init_notebook_mode(connected=True) + + +class Map: + def __init__(self, G): + self._graph = G + self.intersections = nx.get_node_attributes(G, "pos") + self.roads = [list(G[node]) for node in G.nodes()] + + def save(self, filename): + with open(filename, "wb") as f: + pickle.dump(self._graph, f) + + +def load_map(name): + with open(name, "rb") as f: + G = pickle.load(f) + return Map(G) + + +def show_map(M, start=None, goal=None, path=None): + G = M._graph + pos = nx.get_node_attributes(G, "pos") + edge_trace = Scatter( + x=[], + y=[], + line=Line( + width=0.5, + color="#888"), + hoverinfo="none", + mode="lines") + + for edge in G.edges(): + x0, y0 = G.node[edge[0]]["pos"] + x1, y1 = G.node[edge[1]]["pos"] + edge_trace["x"] += [x0, x1, None] + edge_trace["y"] += [y0, y1, None] + + node_trace = Scatter( + x=[], + y=[], + text=[], + mode="markers", + hoverinfo="text", + marker=Marker( + showscale=False, + # colorscale options + # 'Greys' | 'Greens' | 'Bluered' | 'Hot' | 'Picnic' | 'Portland' | + # Jet' | 'RdBu' | 'Blackbody' | 'Earth' | 'Electric' | 'YIOrRd' | + # 'YIGnBu' + colorscale="Hot", + reversescale=True, + color=[], + size=10, + colorbar=dict( + thickness=15, + title="Node Connections", + xanchor="left", + titleside="right", + ), + line=dict(width=2), + ), + ) + for node in G.nodes(): + x, y = G.node[node]["pos"] + node_trace["x"].append(x) + node_trace["y"].append(y) + + for node, adjacencies in enumerate(G.adjacency_list()): + color = 0 + if path and node in path: + color = 2 + if node == start: + color = 3 + elif node == goal: + color = 1 + # node_trace['marker']['color'].append(len(adjacencies)) + node_trace["marker"]["color"].append(color) + node_info = "Intersection " + str(node) + node_trace["text"].append(node_info) + + fig = Figure( + data=Data([edge_trace, node_trace]), + layout=Layout( + title="
Network graph made with Python", + titlefont=dict(size=16), + showlegend=False, + hovermode="closest", + margin=dict(b=20, l=5, r=5, t=40), + xaxis=XAxis(showgrid=False, zeroline=False, showticklabels=False), + yaxis=YAxis(showgrid=False, zeroline=False, showticklabels=False), + ), + ) + + iplot(fig) diff --git a/projects/route_planner/map-10.pickle b/projects/route_planner/map-10.pickle new file mode 100644 index 0000000..0d817ec Binary files /dev/null and b/projects/route_planner/map-10.pickle differ diff --git a/projects/route_planner/map-40.pickle b/projects/route_planner/map-40.pickle new file mode 100644 index 0000000..b44e1b2 Binary files /dev/null and b/projects/route_planner/map-40.pickle differ diff --git a/projects/route_planner/project_notebook.ipynb b/projects/route_planner/project_notebook.ipynb new file mode 100644 index 0000000..1336243 --- /dev/null +++ b/projects/route_planner/project_notebook.ipynb @@ -0,0 +1,2463 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "# Implementing a Route Planner\n", + "In this project you will use A\\* search to implement a \"Google-maps\" style route planning algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], + "source": [ + "# Run this cell first!\n", + "\n", + "from helpers import Map, load_map, show_map\n", + "from student_code import shortest_path\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Map Basics" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "hoverinfo": "none", + "line": { + "color": "#888", + "width": 0.5 + }, + "mode": "lines", + "type": "scatter", + "x": [ + 0.7798606835438107, + 0.46247219371675075, + null, + 0.7798606835438107, + 0.8820353070895344, + null, + 0.7798606835438107, + 0.49016747075266875, + null, + 0.7647837074641568, + 0.8325506249953353, + null, + 0.7647837074641568, + 0.7076566826610747, + null, + 0.7647837074641568, + 0.7155217893995438, + null, + 0.7155217893995438, + 0.8325506249953353, + null, + 0.7155217893995438, + 0.7076566826610747, + null, + 0.7076566826610747, + 0.49016747075266875, + null, + 0.7076566826610747, + 0.8325506249953353, + null, + 0.49016747075266875, + 0.46247219371675075, + null, + 0.11622158839385677, + 0.1285377678230034, + null + ], + "y": [ + 0.6922727646627362, + 0.6258061621642713, + null, + 0.6922727646627362, + 0.6791919587749445, + null, + 0.6922727646627362, + 0.5464878695400415, + null, + 0.3252670836724646, + 0.02310946309985762, + null, + 0.3252670836724646, + 0.3278339270610988, + null, + 0.3252670836724646, + 0.20026498027300055, + null, + 0.20026498027300055, + 0.02310946309985762, + null, + 0.20026498027300055, + 0.3278339270610988, + null, + 0.3278339270610988, + 0.5464878695400415, + null, + 0.3278339270610988, + 0.02310946309985762, + null, + 0.5464878695400415, + 0.6258061621642713, + null, + 0.11236327488812581, + 0.3285840695698353, + null + ] + }, + { + "hoverinfo": "text", + "marker": { + "color": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "colorbar": { + "thickness": 15, + "title": "Node Connections", + "titleside": "right", + "xanchor": "left" + }, + "colorscale": "Hot", + "line": { + "width": 2 + }, + "reversescale": true, + "showscale": false, + "size": 10 + }, + "mode": "markers", + "text": [ + "Intersection 0", + "Intersection 1", + "Intersection 2", + "Intersection 3", + "Intersection 4", + "Intersection 5", + "Intersection 6", + "Intersection 7", + "Intersection 8", + "Intersection 9" + ], + "type": "scatter", + "x": [ + 0.7798606835438107, + 0.7647837074641568, + 0.7155217893995438, + 0.7076566826610747, + 0.8325506249953353, + 0.49016747075266875, + 0.8820353070895344, + 0.46247219371675075, + 0.11622158839385677, + 0.1285377678230034 + ], + "y": [ + 0.6922727646627362, + 0.3252670836724646, + 0.20026498027300055, + 0.3278339270610988, + 0.02310946309985762, + 0.5464878695400415, + 0.6791919587749445, + 0.6258061621642713, + 0.11236327488812581, + 0.3285840695698353 + ] + } + ], + "layout": { + "hovermode": "closest", + "margin": { + "b": 20, + "l": 5, + "r": 5, + "t": 40 + }, + "showlegend": false, + "title": "
Network graph made with Python", + "titlefont": { + "size": 16 + }, + "xaxis": { + "showgrid": false, + "showticklabels": false, + "zeroline": false + }, + "yaxis": { + "showgrid": false, + "showticklabels": false, + "zeroline": false + } + } + }, + "text/html": [ + "
" + ], + "text/vnd.plotly.v1+html": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "map_10 = load_map('map-10.pickle')\n", + "show_map(map_10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The map above (run the code cell if you don't see it) shows a disconnected network of 10 intersections. The two intersections on the left are connected to each other but they are not connected to the rest of the road network. On the graph above, the edge between 2 nodes(intersections) represents a literal straight road not just an abstract connection of 2 cities.\n", + "\n", + "These `Map` objects have two properties you will want to use to implement A\\* search: `intersections` and `roads`\n", + "\n", + "**Intersections**\n", + "\n", + "The `intersections` are represented as a dictionary. \n", + "\n", + "In this example, there are 10 intersections, each identified by an x,y coordinate. The coordinates are listed below. You can hover over each dot in the map above to see the intersection number." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: [0.7798606835438107, 0.6922727646627362],\n", + " 1: [0.7647837074641568, 0.3252670836724646],\n", + " 2: [0.7155217893995438, 0.20026498027300055],\n", + " 3: [0.7076566826610747, 0.3278339270610988],\n", + " 4: [0.8325506249953353, 0.02310946309985762],\n", + " 5: [0.49016747075266875, 0.5464878695400415],\n", + " 6: [0.8820353070895344, 0.6791919587749445],\n", + " 7: [0.46247219371675075, 0.6258061621642713],\n", + " 8: [0.11622158839385677, 0.11236327488812581],\n", + " 9: [0.1285377678230034, 0.3285840695698353]}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "map_10.intersections" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Roads**\n", + "\n", + "The `roads` property is a list where, if `i` is an intersection, `roads[i]` contains a list of the intersections that intersection `i` connects to." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 6, 5]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# this shows that intersection 0 connects to intersections 7, 6, and 5\n", + "map_10.roads[0] " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[7, 6, 5],\n", + " [4, 3, 2],\n", + " [4, 3, 1],\n", + " [5, 4, 1, 2],\n", + " [1, 2, 3],\n", + " [7, 0, 3],\n", + " [0],\n", + " [0, 5],\n", + " [9],\n", + " [8]]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# This shows the full connectivity of the map\n", + "map_10.roads" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "hoverinfo": "none", + "line": { + "color": "#888", + "width": 0.5 + }, + "mode": "lines", + "type": "scatter", + "x": [ + 0.7801603911549438, + 0.740625863119245, + null, + 0.7801603911549438, + 0.583993110207876, + null, + 0.7801603911549438, + 0.640952694324525, + null, + 0.7801603911549438, + 0.6435799740880603, + null, + 0.7801603911549438, + 0.607698913404794, + null, + 0.5249831588690298, + 0.3073865727705063, + null, + 0.5249831588690298, + 0.640952694324525, + null, + 0.5249831588690298, + 0.7482655725962591, + null, + 0.5249831588690298, + 0.5572917679006295, + null, + 0.5249831588690298, + 0.6243437191127235, + null, + 0.5249831588690298, + 0.4238357358399233, + null, + 0.5249831588690298, + 0.719569201584275, + null, + 0.5249831588690298, + 0.607698913404794, + null, + 0.5249831588690298, + 0.4582523173083307, + null, + 0.5249831588690298, + 0.313999018186756, + null, + 0.8085335344099086, + 0.6315322816286787, + null, + 0.8085335344099086, + 0.740625863119245, + null, + 0.8085335344099086, + 0.8252497121120052, + null, + 0.8085335344099086, + 0.8860336256842246, + null, + 0.8085335344099086, + 0.8551947714242674, + null, + 0.8085335344099086, + 0.6824813442515916, + null, + 0.8085335344099086, + 0.7353838928272886, + null, + 0.2599134798656856, + 0.3073865727705063, + null, + 0.2599134798656856, + 0.4238357358399233, + null, + 0.2599134798656856, + 0.4582523173083307, + null, + 0.2599134798656856, + 0.25311953895059136, + null, + 0.2599134798656856, + 0.313999018186756, + null, + 0.7353838928272886, + 0.6315322816286787, + null, + 0.7353838928272886, + 0.740625863119245, + null, + 0.7353838928272886, + 0.8252497121120052, + null, + 0.7353838928272886, + 0.8860336256842246, + null, + 0.7353838928272886, + 0.8551947714242674, + null, + 0.7353838928272886, + 0.6824813442515916, + null, + 0.09088671576431506, + 0.17440205342790494, + null, + 0.09088671576431506, + 0.12939557977525573, + null, + 0.09088671576431506, + 0.04580558670435442, + null, + 0.313999018186756, + 0.3073865727705063, + null, + 0.313999018186756, + 0.4238357358399233, + null, + 0.313999018186756, + 0.4582523173083307, + null, + 0.313999018186756, + 0.25311953895059136, + null, + 0.6824813442515916, + 0.6315322816286787, + null, + 0.6824813442515916, + 0.740625863119245, + null, + 0.6824813442515916, + 0.47415009287034726, + null, + 0.6824813442515916, + 0.8252497121120052, + null, + 0.6824813442515916, + 0.8860336256842246, + null, + 0.6824813442515916, + 0.8551947714242674, + null, + 0.20128789391122526, + 0.1332965908314021, + null, + 0.20128789391122526, + 0.021423673670808885, + null, + 0.20128789391122526, + 0.04580558670435442, + null, + 0.8551947714242674, + 0.740625863119245, + null, + 0.8551947714242674, + 0.8252497121120052, + null, + 0.8551947714242674, + 0.8860336256842246, + null, + 0.7581736589784409, + 0.640952694324525, + null, + 0.7581736589784409, + 0.7482655725962591, + null, + 0.7581736589784409, + 0.5572917679006295, + null, + 0.7581736589784409, + 0.6243437191127235, + null, + 0.7581736589784409, + 0.9363713903322148, + null, + 0.7581736589784409, + 0.719569201584275, + null, + 0.7581736589784409, + 0.607698913404794, + null, + 0.7581736589784409, + 0.9112422509614865, + null, + 0.25311953895059136, + 0.3073865727705063, + null, + 0.25311953895059136, + 0.4238357358399233, + null, + 0.25311953895059136, + 0.4582523173083307, + null, + 0.4813859169876731, + 0.3345284735051981, + null, + 0.4813859169876731, + 0.583993110207876, + null, + 0.4813859169876731, + 0.640952694324525, + null, + 0.4813859169876731, + 0.6435799740880603, + null, + 0.4813859169876731, + 0.47415009287034726, + null, + 0.4813859169876731, + 0.607698913404794, + null, + 0.9112422509614865, + 0.7482655725962591, + null, + 0.9112422509614865, + 0.9363713903322148, + null, + 0.9112422509614865, + 0.719569201584275, + null, + 0.04580558670435442, + 0.1332965908314021, + null, + 0.04580558670435442, + 0.021423673670808885, + null, + 0.04580558670435442, + 0.12939557977525573, + null, + 0.4582523173083307, + 0.3073865727705063, + null, + 0.4582523173083307, + 0.640952694324525, + null, + 0.4582523173083307, + 0.5572917679006295, + null, + 0.4582523173083307, + 0.6243437191127235, + null, + 0.4582523173083307, + 0.4238357358399233, + null, + 0.4582523173083307, + 0.607698913404794, + null, + 0.12939557977525573, + 0.3345284735051981, + null, + 0.12939557977525573, + 0.021423673670808885, + null, + 0.607698913404794, + 0.583993110207876, + null, + 0.607698913404794, + 0.640952694324525, + null, + 0.607698913404794, + 0.6435799740880603, + null, + 0.607698913404794, + 0.5572917679006295, + null, + 0.607698913404794, + 0.6243437191127235, + null, + 0.607698913404794, + 0.719569201584275, + null, + 0.719569201584275, + 0.640952694324525, + null, + 0.719569201584275, + 0.7482655725962591, + null, + 0.719569201584275, + 0.5572917679006295, + null, + 0.719569201584275, + 0.6243437191127235, + null, + 0.719569201584275, + 0.9363713903322148, + null, + 0.8860336256842246, + 0.8252497121120052, + null, + 0.4238357358399233, + 0.3073865727705063, + null, + 0.4238357358399233, + 0.5572917679006295, + null, + 0.47415009287034726, + 0.6315322816286787, + null, + 0.47415009287034726, + 0.3345284735051981, + null, + 0.47415009287034726, + 0.34509802713919313, + null, + 0.26253385360950576, + 0.17972981733780147, + null, + 0.26253385360950576, + 0.17440205342790494, + null, + 0.26253385360950576, + 0.34509802713919313, + null, + 0.9363713903322148, + 0.7482655725962591, + null, + 0.6243437191127235, + 0.583993110207876, + null, + 0.6243437191127235, + 0.640952694324525, + null, + 0.6243437191127235, + 0.7482655725962591, + null, + 0.6243437191127235, + 0.5572917679006295, + null, + 0.5572917679006295, + 0.583993110207876, + null, + 0.5572917679006295, + 0.640952694324525, + null, + 0.5572917679006295, + 0.7482655725962591, + null, + 0.7482655725962591, + 0.640952694324525, + null, + 0.6435799740880603, + 0.6315322816286787, + null, + 0.6435799740880603, + 0.740625863119245, + null, + 0.6435799740880603, + 0.583993110207876, + null, + 0.6435799740880603, + 0.640952694324525, + null, + 0.34509802713919313, + 0.17972981733780147, + null, + 0.34509802713919313, + 0.3345284735051981, + null, + 0.34509802713919313, + 0.17440205342790494, + null, + 0.021423673670808885, + 0.1332965908314021, + null, + 0.640952694324525, + 0.583993110207876, + null, + 0.17440205342790494, + 0.17972981733780147, + null, + 0.740625863119245, + 0.6315322816286787, + null + ], + "y": [ + 0.49474860768712914, + 0.68128520136847, + null, + 0.49474860768712914, + 0.42704536740474663, + null, + 0.49474860768712914, + 0.3232711412508066, + null, + 0.49474860768712914, + 0.5488515965193208, + null, + 0.49474860768712914, + 0.362322730884702, + null, + 0.14953665513987202, + 0.09186645974288632, + null, + 0.14953665513987202, + 0.3232711412508066, + null, + 0.14953665513987202, + 0.12631654071213483, + null, + 0.14953665513987202, + 0.2083567880838434, + null, + 0.14953665513987202, + 0.21665962402659544, + null, + 0.14953665513987202, + 0.026771817842421997, + null, + 0.14953665513987202, + 0.13985272363426526, + null, + 0.14953665513987202, + 0.362322730884702, + null, + 0.14953665513987202, + 0.1735506267461867, + null, + 0.14953665513987202, + 0.01876171413125327, + null, + 0.7696330846542071, + 0.7311657634689946, + null, + 0.7696330846542071, + 0.68128520136847, + null, + 0.7696330846542071, + 0.9532681441921305, + null, + 0.7696330846542071, + 0.891868301175821, + null, + 0.7696330846542071, + 0.9011339078096633, + null, + 0.7696330846542071, + 0.8016111783687677, + null, + 0.7696330846542071, + 0.8089961609345658, + null, + 0.14485659826020547, + 0.09186645974288632, + null, + 0.14485659826020547, + 0.026771817842421997, + null, + 0.14485659826020547, + 0.1735506267461867, + null, + 0.14485659826020547, + 0.10321622277398101, + null, + 0.14485659826020547, + 0.01876171413125327, + null, + 0.8089961609345658, + 0.7311657634689946, + null, + 0.8089961609345658, + 0.68128520136847, + null, + 0.8089961609345658, + 0.9532681441921305, + null, + 0.8089961609345658, + 0.891868301175821, + null, + 0.8089961609345658, + 0.9011339078096633, + null, + 0.8089961609345658, + 0.8016111783687677, + null, + 0.7222846879290787, + 0.9528527425842739, + null, + 0.7222846879290787, + 0.690016328140396, + null, + 0.7222846879290787, + 0.5886703168399895, + null, + 0.01876171413125327, + 0.09186645974288632, + null, + 0.01876171413125327, + 0.026771817842421997, + null, + 0.01876171413125327, + 0.1735506267461867, + null, + 0.01876171413125327, + 0.10321622277398101, + null, + 0.8016111783687677, + 0.7311657634689946, + null, + 0.8016111783687677, + 0.68128520136847, + null, + 0.8016111783687677, + 0.7353428557575755, + null, + 0.8016111783687677, + 0.9532681441921305, + null, + 0.8016111783687677, + 0.891868301175821, + null, + 0.8016111783687677, + 0.9011339078096633, + null, + 0.43196344222361227, + 0.3996510641743197, + null, + 0.43196344222361227, + 0.4666482714834408, + null, + 0.43196344222361227, + 0.5886703168399895, + null, + 0.9011339078096633, + 0.68128520136847, + null, + 0.9011339078096633, + 0.9532681441921305, + null, + 0.9011339078096633, + 0.891868301175821, + null, + 0.24026772497187532, + 0.3232711412508066, + null, + 0.24026772497187532, + 0.12631654071213483, + null, + 0.24026772497187532, + 0.2083567880838434, + null, + 0.24026772497187532, + 0.21665962402659544, + null, + 0.24026772497187532, + 0.13022993020357043, + null, + 0.24026772497187532, + 0.13985272363426526, + null, + 0.24026772497187532, + 0.362322730884702, + null, + 0.24026772497187532, + 0.1839028760606296, + null, + 0.10321622277398101, + 0.09186645974288632, + null, + 0.10321622277398101, + 0.026771817842421997, + null, + 0.10321622277398101, + 0.1735506267461867, + null, + 0.5006237737207431, + 0.6569436279895382, + null, + 0.5006237737207431, + 0.42704536740474663, + null, + 0.5006237737207431, + 0.3232711412508066, + null, + 0.5006237737207431, + 0.5488515965193208, + null, + 0.5006237737207431, + 0.7353428557575755, + null, + 0.5006237737207431, + 0.362322730884702, + null, + 0.1839028760606296, + 0.12631654071213483, + null, + 0.1839028760606296, + 0.13022993020357043, + null, + 0.1839028760606296, + 0.13985272363426526, + null, + 0.5886703168399895, + 0.3996510641743197, + null, + 0.5886703168399895, + 0.4666482714834408, + null, + 0.5886703168399895, + 0.690016328140396, + null, + 0.1735506267461867, + 0.09186645974288632, + null, + 0.1735506267461867, + 0.3232711412508066, + null, + 0.1735506267461867, + 0.2083567880838434, + null, + 0.1735506267461867, + 0.21665962402659544, + null, + 0.1735506267461867, + 0.026771817842421997, + null, + 0.1735506267461867, + 0.362322730884702, + null, + 0.690016328140396, + 0.6569436279895382, + null, + 0.690016328140396, + 0.4666482714834408, + null, + 0.362322730884702, + 0.42704536740474663, + null, + 0.362322730884702, + 0.3232711412508066, + null, + 0.362322730884702, + 0.5488515965193208, + null, + 0.362322730884702, + 0.2083567880838434, + null, + 0.362322730884702, + 0.21665962402659544, + null, + 0.362322730884702, + 0.13985272363426526, + null, + 0.13985272363426526, + 0.3232711412508066, + null, + 0.13985272363426526, + 0.12631654071213483, + null, + 0.13985272363426526, + 0.2083567880838434, + null, + 0.13985272363426526, + 0.21665962402659544, + null, + 0.13985272363426526, + 0.13022993020357043, + null, + 0.891868301175821, + 0.9532681441921305, + null, + 0.026771817842421997, + 0.09186645974288632, + null, + 0.026771817842421997, + 0.2083567880838434, + null, + 0.7353428557575755, + 0.7311657634689946, + null, + 0.7353428557575755, + 0.6569436279895382, + null, + 0.7353428557575755, + 0.8800306496459869, + null, + 0.9768234503830939, + 0.999395685828547, + null, + 0.9768234503830939, + 0.9528527425842739, + null, + 0.9768234503830939, + 0.8800306496459869, + null, + 0.13022993020357043, + 0.12631654071213483, + null, + 0.21665962402659544, + 0.42704536740474663, + null, + 0.21665962402659544, + 0.3232711412508066, + null, + 0.21665962402659544, + 0.12631654071213483, + null, + 0.21665962402659544, + 0.2083567880838434, + null, + 0.2083567880838434, + 0.42704536740474663, + null, + 0.2083567880838434, + 0.3232711412508066, + null, + 0.2083567880838434, + 0.12631654071213483, + null, + 0.12631654071213483, + 0.3232711412508066, + null, + 0.5488515965193208, + 0.7311657634689946, + null, + 0.5488515965193208, + 0.68128520136847, + null, + 0.5488515965193208, + 0.42704536740474663, + null, + 0.5488515965193208, + 0.3232711412508066, + null, + 0.8800306496459869, + 0.999395685828547, + null, + 0.8800306496459869, + 0.6569436279895382, + null, + 0.8800306496459869, + 0.9528527425842739, + null, + 0.4666482714834408, + 0.3996510641743197, + null, + 0.3232711412508066, + 0.42704536740474663, + null, + 0.9528527425842739, + 0.999395685828547, + null, + 0.68128520136847, + 0.7311657634689946, + null + ] + }, + { + "hoverinfo": "text", + "marker": { + "color": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "colorbar": { + "thickness": 15, + "title": "Node Connections", + "titleside": "right", + "xanchor": "left" + }, + "colorscale": "Hot", + "line": { + "width": 2 + }, + "reversescale": true, + "showscale": false, + "size": 10 + }, + "mode": "markers", + "text": [ + "Intersection 0", + "Intersection 1", + "Intersection 2", + "Intersection 3", + "Intersection 4", + "Intersection 5", + "Intersection 6", + "Intersection 7", + "Intersection 8", + "Intersection 9", + "Intersection 10", + "Intersection 11", + "Intersection 12", + "Intersection 13", + "Intersection 14", + "Intersection 15", + "Intersection 16", + "Intersection 17", + "Intersection 18", + "Intersection 19", + "Intersection 20", + "Intersection 21", + "Intersection 22", + "Intersection 23", + "Intersection 24", + "Intersection 25", + "Intersection 26", + "Intersection 27", + "Intersection 28", + "Intersection 29", + "Intersection 30", + "Intersection 31", + "Intersection 32", + "Intersection 33", + "Intersection 34", + "Intersection 35", + "Intersection 36", + "Intersection 37", + "Intersection 38", + "Intersection 39" + ], + "type": "scatter", + "x": [ + 0.7801603911549438, + 0.5249831588690298, + 0.8085335344099086, + 0.2599134798656856, + 0.7353838928272886, + 0.09088671576431506, + 0.313999018186756, + 0.6824813442515916, + 0.20128789391122526, + 0.8551947714242674, + 0.7581736589784409, + 0.25311953895059136, + 0.4813859169876731, + 0.9112422509614865, + 0.04580558670435442, + 0.4582523173083307, + 0.12939557977525573, + 0.607698913404794, + 0.719569201584275, + 0.8860336256842246, + 0.4238357358399233, + 0.8252497121120052, + 0.47415009287034726, + 0.26253385360950576, + 0.9363713903322148, + 0.6243437191127235, + 0.5572917679006295, + 0.7482655725962591, + 0.6435799740880603, + 0.34509802713919313, + 0.021423673670808885, + 0.640952694324525, + 0.17440205342790494, + 0.1332965908314021, + 0.583993110207876, + 0.3073865727705063, + 0.740625863119245, + 0.3345284735051981, + 0.17972981733780147, + 0.6315322816286787 + ], + "y": [ + 0.49474860768712914, + 0.14953665513987202, + 0.7696330846542071, + 0.14485659826020547, + 0.8089961609345658, + 0.7222846879290787, + 0.01876171413125327, + 0.8016111783687677, + 0.43196344222361227, + 0.9011339078096633, + 0.24026772497187532, + 0.10321622277398101, + 0.5006237737207431, + 0.1839028760606296, + 0.5886703168399895, + 0.1735506267461867, + 0.690016328140396, + 0.362322730884702, + 0.13985272363426526, + 0.891868301175821, + 0.026771817842421997, + 0.9532681441921305, + 0.7353428557575755, + 0.9768234503830939, + 0.13022993020357043, + 0.21665962402659544, + 0.2083567880838434, + 0.12631654071213483, + 0.5488515965193208, + 0.8800306496459869, + 0.4666482714834408, + 0.3232711412508066, + 0.9528527425842739, + 0.3996510641743197, + 0.42704536740474663, + 0.09186645974288632, + 0.68128520136847, + 0.6569436279895382, + 0.999395685828547, + 0.7311657634689946 + ] + } + ], + "layout": { + "hovermode": "closest", + "margin": { + "b": 20, + "l": 5, + "r": 5, + "t": 40 + }, + "showlegend": false, + "title": "
Network graph made with Python", + "titlefont": { + "size": 16 + }, + "xaxis": { + "showgrid": false, + "showticklabels": false, + "zeroline": false + }, + "yaxis": { + "showgrid": false, + "showticklabels": false, + "zeroline": false + } + } + }, + "text/html": [ + "
" + ], + "text/vnd.plotly.v1+html": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# map_40 is a bigger map than map_10\n", + "map_40 = load_map('map-40.pickle')\n", + "show_map(map_40)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Advanced Visualizations\n", + "\n", + "The map above shows a network of roads which spans 40 different intersections (labeled 0 through 39). \n", + "\n", + "The `show_map` function which generated this map also takes a few optional parameters which might be useful for visualizaing the output of the search algorithm you will write.\n", + "\n", + "* `start` - The \"start\" node for the search algorithm.\n", + "* `goal` - The \"goal\" node.\n", + "* `path` - An array of integers which corresponds to a valid sequence of intersection visits on the map." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "hoverinfo": "none", + "line": { + "color": "#888", + "width": 0.5 + }, + "mode": "lines", + "type": "scatter", + "x": [ + 0.7801603911549438, + 0.740625863119245, + null, + 0.7801603911549438, + 0.583993110207876, + null, + 0.7801603911549438, + 0.640952694324525, + null, + 0.7801603911549438, + 0.6435799740880603, + null, + 0.7801603911549438, + 0.607698913404794, + null, + 0.5249831588690298, + 0.3073865727705063, + null, + 0.5249831588690298, + 0.640952694324525, + null, + 0.5249831588690298, + 0.7482655725962591, + null, + 0.5249831588690298, + 0.5572917679006295, + null, + 0.5249831588690298, + 0.6243437191127235, + null, + 0.5249831588690298, + 0.4238357358399233, + null, + 0.5249831588690298, + 0.719569201584275, + null, + 0.5249831588690298, + 0.607698913404794, + null, + 0.5249831588690298, + 0.4582523173083307, + null, + 0.5249831588690298, + 0.313999018186756, + null, + 0.8085335344099086, + 0.6315322816286787, + null, + 0.8085335344099086, + 0.740625863119245, + null, + 0.8085335344099086, + 0.8252497121120052, + null, + 0.8085335344099086, + 0.8860336256842246, + null, + 0.8085335344099086, + 0.8551947714242674, + null, + 0.8085335344099086, + 0.6824813442515916, + null, + 0.8085335344099086, + 0.7353838928272886, + null, + 0.2599134798656856, + 0.3073865727705063, + null, + 0.2599134798656856, + 0.4238357358399233, + null, + 0.2599134798656856, + 0.4582523173083307, + null, + 0.2599134798656856, + 0.25311953895059136, + null, + 0.2599134798656856, + 0.313999018186756, + null, + 0.7353838928272886, + 0.6315322816286787, + null, + 0.7353838928272886, + 0.740625863119245, + null, + 0.7353838928272886, + 0.8252497121120052, + null, + 0.7353838928272886, + 0.8860336256842246, + null, + 0.7353838928272886, + 0.8551947714242674, + null, + 0.7353838928272886, + 0.6824813442515916, + null, + 0.09088671576431506, + 0.17440205342790494, + null, + 0.09088671576431506, + 0.12939557977525573, + null, + 0.09088671576431506, + 0.04580558670435442, + null, + 0.313999018186756, + 0.3073865727705063, + null, + 0.313999018186756, + 0.4238357358399233, + null, + 0.313999018186756, + 0.4582523173083307, + null, + 0.313999018186756, + 0.25311953895059136, + null, + 0.6824813442515916, + 0.6315322816286787, + null, + 0.6824813442515916, + 0.740625863119245, + null, + 0.6824813442515916, + 0.47415009287034726, + null, + 0.6824813442515916, + 0.8252497121120052, + null, + 0.6824813442515916, + 0.8860336256842246, + null, + 0.6824813442515916, + 0.8551947714242674, + null, + 0.20128789391122526, + 0.1332965908314021, + null, + 0.20128789391122526, + 0.021423673670808885, + null, + 0.20128789391122526, + 0.04580558670435442, + null, + 0.8551947714242674, + 0.740625863119245, + null, + 0.8551947714242674, + 0.8252497121120052, + null, + 0.8551947714242674, + 0.8860336256842246, + null, + 0.7581736589784409, + 0.640952694324525, + null, + 0.7581736589784409, + 0.7482655725962591, + null, + 0.7581736589784409, + 0.5572917679006295, + null, + 0.7581736589784409, + 0.6243437191127235, + null, + 0.7581736589784409, + 0.9363713903322148, + null, + 0.7581736589784409, + 0.719569201584275, + null, + 0.7581736589784409, + 0.607698913404794, + null, + 0.7581736589784409, + 0.9112422509614865, + null, + 0.25311953895059136, + 0.3073865727705063, + null, + 0.25311953895059136, + 0.4238357358399233, + null, + 0.25311953895059136, + 0.4582523173083307, + null, + 0.4813859169876731, + 0.3345284735051981, + null, + 0.4813859169876731, + 0.583993110207876, + null, + 0.4813859169876731, + 0.640952694324525, + null, + 0.4813859169876731, + 0.6435799740880603, + null, + 0.4813859169876731, + 0.47415009287034726, + null, + 0.4813859169876731, + 0.607698913404794, + null, + 0.9112422509614865, + 0.7482655725962591, + null, + 0.9112422509614865, + 0.9363713903322148, + null, + 0.9112422509614865, + 0.719569201584275, + null, + 0.04580558670435442, + 0.1332965908314021, + null, + 0.04580558670435442, + 0.021423673670808885, + null, + 0.04580558670435442, + 0.12939557977525573, + null, + 0.4582523173083307, + 0.3073865727705063, + null, + 0.4582523173083307, + 0.640952694324525, + null, + 0.4582523173083307, + 0.5572917679006295, + null, + 0.4582523173083307, + 0.6243437191127235, + null, + 0.4582523173083307, + 0.4238357358399233, + null, + 0.4582523173083307, + 0.607698913404794, + null, + 0.12939557977525573, + 0.3345284735051981, + null, + 0.12939557977525573, + 0.021423673670808885, + null, + 0.607698913404794, + 0.583993110207876, + null, + 0.607698913404794, + 0.640952694324525, + null, + 0.607698913404794, + 0.6435799740880603, + null, + 0.607698913404794, + 0.5572917679006295, + null, + 0.607698913404794, + 0.6243437191127235, + null, + 0.607698913404794, + 0.719569201584275, + null, + 0.719569201584275, + 0.640952694324525, + null, + 0.719569201584275, + 0.7482655725962591, + null, + 0.719569201584275, + 0.5572917679006295, + null, + 0.719569201584275, + 0.6243437191127235, + null, + 0.719569201584275, + 0.9363713903322148, + null, + 0.8860336256842246, + 0.8252497121120052, + null, + 0.4238357358399233, + 0.3073865727705063, + null, + 0.4238357358399233, + 0.5572917679006295, + null, + 0.47415009287034726, + 0.6315322816286787, + null, + 0.47415009287034726, + 0.3345284735051981, + null, + 0.47415009287034726, + 0.34509802713919313, + null, + 0.26253385360950576, + 0.17972981733780147, + null, + 0.26253385360950576, + 0.17440205342790494, + null, + 0.26253385360950576, + 0.34509802713919313, + null, + 0.9363713903322148, + 0.7482655725962591, + null, + 0.6243437191127235, + 0.583993110207876, + null, + 0.6243437191127235, + 0.640952694324525, + null, + 0.6243437191127235, + 0.7482655725962591, + null, + 0.6243437191127235, + 0.5572917679006295, + null, + 0.5572917679006295, + 0.583993110207876, + null, + 0.5572917679006295, + 0.640952694324525, + null, + 0.5572917679006295, + 0.7482655725962591, + null, + 0.7482655725962591, + 0.640952694324525, + null, + 0.6435799740880603, + 0.6315322816286787, + null, + 0.6435799740880603, + 0.740625863119245, + null, + 0.6435799740880603, + 0.583993110207876, + null, + 0.6435799740880603, + 0.640952694324525, + null, + 0.34509802713919313, + 0.17972981733780147, + null, + 0.34509802713919313, + 0.3345284735051981, + null, + 0.34509802713919313, + 0.17440205342790494, + null, + 0.021423673670808885, + 0.1332965908314021, + null, + 0.640952694324525, + 0.583993110207876, + null, + 0.17440205342790494, + 0.17972981733780147, + null, + 0.740625863119245, + 0.6315322816286787, + null + ], + "y": [ + 0.49474860768712914, + 0.68128520136847, + null, + 0.49474860768712914, + 0.42704536740474663, + null, + 0.49474860768712914, + 0.3232711412508066, + null, + 0.49474860768712914, + 0.5488515965193208, + null, + 0.49474860768712914, + 0.362322730884702, + null, + 0.14953665513987202, + 0.09186645974288632, + null, + 0.14953665513987202, + 0.3232711412508066, + null, + 0.14953665513987202, + 0.12631654071213483, + null, + 0.14953665513987202, + 0.2083567880838434, + null, + 0.14953665513987202, + 0.21665962402659544, + null, + 0.14953665513987202, + 0.026771817842421997, + null, + 0.14953665513987202, + 0.13985272363426526, + null, + 0.14953665513987202, + 0.362322730884702, + null, + 0.14953665513987202, + 0.1735506267461867, + null, + 0.14953665513987202, + 0.01876171413125327, + null, + 0.7696330846542071, + 0.7311657634689946, + null, + 0.7696330846542071, + 0.68128520136847, + null, + 0.7696330846542071, + 0.9532681441921305, + null, + 0.7696330846542071, + 0.891868301175821, + null, + 0.7696330846542071, + 0.9011339078096633, + null, + 0.7696330846542071, + 0.8016111783687677, + null, + 0.7696330846542071, + 0.8089961609345658, + null, + 0.14485659826020547, + 0.09186645974288632, + null, + 0.14485659826020547, + 0.026771817842421997, + null, + 0.14485659826020547, + 0.1735506267461867, + null, + 0.14485659826020547, + 0.10321622277398101, + null, + 0.14485659826020547, + 0.01876171413125327, + null, + 0.8089961609345658, + 0.7311657634689946, + null, + 0.8089961609345658, + 0.68128520136847, + null, + 0.8089961609345658, + 0.9532681441921305, + null, + 0.8089961609345658, + 0.891868301175821, + null, + 0.8089961609345658, + 0.9011339078096633, + null, + 0.8089961609345658, + 0.8016111783687677, + null, + 0.7222846879290787, + 0.9528527425842739, + null, + 0.7222846879290787, + 0.690016328140396, + null, + 0.7222846879290787, + 0.5886703168399895, + null, + 0.01876171413125327, + 0.09186645974288632, + null, + 0.01876171413125327, + 0.026771817842421997, + null, + 0.01876171413125327, + 0.1735506267461867, + null, + 0.01876171413125327, + 0.10321622277398101, + null, + 0.8016111783687677, + 0.7311657634689946, + null, + 0.8016111783687677, + 0.68128520136847, + null, + 0.8016111783687677, + 0.7353428557575755, + null, + 0.8016111783687677, + 0.9532681441921305, + null, + 0.8016111783687677, + 0.891868301175821, + null, + 0.8016111783687677, + 0.9011339078096633, + null, + 0.43196344222361227, + 0.3996510641743197, + null, + 0.43196344222361227, + 0.4666482714834408, + null, + 0.43196344222361227, + 0.5886703168399895, + null, + 0.9011339078096633, + 0.68128520136847, + null, + 0.9011339078096633, + 0.9532681441921305, + null, + 0.9011339078096633, + 0.891868301175821, + null, + 0.24026772497187532, + 0.3232711412508066, + null, + 0.24026772497187532, + 0.12631654071213483, + null, + 0.24026772497187532, + 0.2083567880838434, + null, + 0.24026772497187532, + 0.21665962402659544, + null, + 0.24026772497187532, + 0.13022993020357043, + null, + 0.24026772497187532, + 0.13985272363426526, + null, + 0.24026772497187532, + 0.362322730884702, + null, + 0.24026772497187532, + 0.1839028760606296, + null, + 0.10321622277398101, + 0.09186645974288632, + null, + 0.10321622277398101, + 0.026771817842421997, + null, + 0.10321622277398101, + 0.1735506267461867, + null, + 0.5006237737207431, + 0.6569436279895382, + null, + 0.5006237737207431, + 0.42704536740474663, + null, + 0.5006237737207431, + 0.3232711412508066, + null, + 0.5006237737207431, + 0.5488515965193208, + null, + 0.5006237737207431, + 0.7353428557575755, + null, + 0.5006237737207431, + 0.362322730884702, + null, + 0.1839028760606296, + 0.12631654071213483, + null, + 0.1839028760606296, + 0.13022993020357043, + null, + 0.1839028760606296, + 0.13985272363426526, + null, + 0.5886703168399895, + 0.3996510641743197, + null, + 0.5886703168399895, + 0.4666482714834408, + null, + 0.5886703168399895, + 0.690016328140396, + null, + 0.1735506267461867, + 0.09186645974288632, + null, + 0.1735506267461867, + 0.3232711412508066, + null, + 0.1735506267461867, + 0.2083567880838434, + null, + 0.1735506267461867, + 0.21665962402659544, + null, + 0.1735506267461867, + 0.026771817842421997, + null, + 0.1735506267461867, + 0.362322730884702, + null, + 0.690016328140396, + 0.6569436279895382, + null, + 0.690016328140396, + 0.4666482714834408, + null, + 0.362322730884702, + 0.42704536740474663, + null, + 0.362322730884702, + 0.3232711412508066, + null, + 0.362322730884702, + 0.5488515965193208, + null, + 0.362322730884702, + 0.2083567880838434, + null, + 0.362322730884702, + 0.21665962402659544, + null, + 0.362322730884702, + 0.13985272363426526, + null, + 0.13985272363426526, + 0.3232711412508066, + null, + 0.13985272363426526, + 0.12631654071213483, + null, + 0.13985272363426526, + 0.2083567880838434, + null, + 0.13985272363426526, + 0.21665962402659544, + null, + 0.13985272363426526, + 0.13022993020357043, + null, + 0.891868301175821, + 0.9532681441921305, + null, + 0.026771817842421997, + 0.09186645974288632, + null, + 0.026771817842421997, + 0.2083567880838434, + null, + 0.7353428557575755, + 0.7311657634689946, + null, + 0.7353428557575755, + 0.6569436279895382, + null, + 0.7353428557575755, + 0.8800306496459869, + null, + 0.9768234503830939, + 0.999395685828547, + null, + 0.9768234503830939, + 0.9528527425842739, + null, + 0.9768234503830939, + 0.8800306496459869, + null, + 0.13022993020357043, + 0.12631654071213483, + null, + 0.21665962402659544, + 0.42704536740474663, + null, + 0.21665962402659544, + 0.3232711412508066, + null, + 0.21665962402659544, + 0.12631654071213483, + null, + 0.21665962402659544, + 0.2083567880838434, + null, + 0.2083567880838434, + 0.42704536740474663, + null, + 0.2083567880838434, + 0.3232711412508066, + null, + 0.2083567880838434, + 0.12631654071213483, + null, + 0.12631654071213483, + 0.3232711412508066, + null, + 0.5488515965193208, + 0.7311657634689946, + null, + 0.5488515965193208, + 0.68128520136847, + null, + 0.5488515965193208, + 0.42704536740474663, + null, + 0.5488515965193208, + 0.3232711412508066, + null, + 0.8800306496459869, + 0.999395685828547, + null, + 0.8800306496459869, + 0.6569436279895382, + null, + 0.8800306496459869, + 0.9528527425842739, + null, + 0.4666482714834408, + 0.3996510641743197, + null, + 0.3232711412508066, + 0.42704536740474663, + null, + 0.9528527425842739, + 0.999395685828547, + null, + 0.68128520136847, + 0.7311657634689946, + null + ] + }, + { + "hoverinfo": "text", + "marker": { + "color": [ + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 2, + 0, + 0 + ], + "colorbar": { + "thickness": 15, + "title": "Node Connections", + "titleside": "right", + "xanchor": "left" + }, + "colorscale": "Hot", + "line": { + "width": 2 + }, + "reversescale": true, + "showscale": false, + "size": 10 + }, + "mode": "markers", + "text": [ + "Intersection 0", + "Intersection 1", + "Intersection 2", + "Intersection 3", + "Intersection 4", + "Intersection 5", + "Intersection 6", + "Intersection 7", + "Intersection 8", + "Intersection 9", + "Intersection 10", + "Intersection 11", + "Intersection 12", + "Intersection 13", + "Intersection 14", + "Intersection 15", + "Intersection 16", + "Intersection 17", + "Intersection 18", + "Intersection 19", + "Intersection 20", + "Intersection 21", + "Intersection 22", + "Intersection 23", + "Intersection 24", + "Intersection 25", + "Intersection 26", + "Intersection 27", + "Intersection 28", + "Intersection 29", + "Intersection 30", + "Intersection 31", + "Intersection 32", + "Intersection 33", + "Intersection 34", + "Intersection 35", + "Intersection 36", + "Intersection 37", + "Intersection 38", + "Intersection 39" + ], + "type": "scatter", + "x": [ + 0.7801603911549438, + 0.5249831588690298, + 0.8085335344099086, + 0.2599134798656856, + 0.7353838928272886, + 0.09088671576431506, + 0.313999018186756, + 0.6824813442515916, + 0.20128789391122526, + 0.8551947714242674, + 0.7581736589784409, + 0.25311953895059136, + 0.4813859169876731, + 0.9112422509614865, + 0.04580558670435442, + 0.4582523173083307, + 0.12939557977525573, + 0.607698913404794, + 0.719569201584275, + 0.8860336256842246, + 0.4238357358399233, + 0.8252497121120052, + 0.47415009287034726, + 0.26253385360950576, + 0.9363713903322148, + 0.6243437191127235, + 0.5572917679006295, + 0.7482655725962591, + 0.6435799740880603, + 0.34509802713919313, + 0.021423673670808885, + 0.640952694324525, + 0.17440205342790494, + 0.1332965908314021, + 0.583993110207876, + 0.3073865727705063, + 0.740625863119245, + 0.3345284735051981, + 0.17972981733780147, + 0.6315322816286787 + ], + "y": [ + 0.49474860768712914, + 0.14953665513987202, + 0.7696330846542071, + 0.14485659826020547, + 0.8089961609345658, + 0.7222846879290787, + 0.01876171413125327, + 0.8016111783687677, + 0.43196344222361227, + 0.9011339078096633, + 0.24026772497187532, + 0.10321622277398101, + 0.5006237737207431, + 0.1839028760606296, + 0.5886703168399895, + 0.1735506267461867, + 0.690016328140396, + 0.362322730884702, + 0.13985272363426526, + 0.891868301175821, + 0.026771817842421997, + 0.9532681441921305, + 0.7353428557575755, + 0.9768234503830939, + 0.13022993020357043, + 0.21665962402659544, + 0.2083567880838434, + 0.12631654071213483, + 0.5488515965193208, + 0.8800306496459869, + 0.4666482714834408, + 0.3232711412508066, + 0.9528527425842739, + 0.3996510641743197, + 0.42704536740474663, + 0.09186645974288632, + 0.68128520136847, + 0.6569436279895382, + 0.999395685828547, + 0.7311657634689946 + ] + } + ], + "layout": { + "hovermode": "closest", + "margin": { + "b": 20, + "l": 5, + "r": 5, + "t": 40 + }, + "showlegend": false, + "title": "
Network graph made with Python", + "titlefont": { + "size": 16 + }, + "xaxis": { + "showgrid": false, + "showticklabels": false, + "zeroline": false + }, + "yaxis": { + "showgrid": false, + "showticklabels": false, + "zeroline": false + } + } + }, + "text/html": [ + "
" + ], + "text/vnd.plotly.v1+html": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# run this code, note the effect of including the optional\n", + "# parameters in the function call.\n", + "show_map(map_40, start=5, goal=34, path=[5,16,37,12,34])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Writing your algorithm\n", + "You should open the file `student_code.py` in another tab and work on your algorithm there. Do that by selecting `File > Open` and then selecting the appropriate file.\n", + "\n", + "The algorithm you write will be responsible for generating a `path` like the one passed into `show_map` above. In fact, when called with the same map, start and goal, as above you algorithm should produce the path `[5, 16, 37, 12, 34]`\n", + "\n", + "```bash\n", + "> shortest_path(map_40, 5, 34)\n", + "[5, 16, 37, 12, 34]\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "great! Your code works for these inputs!\n" + ] + } + ], + "source": [ + "path = shortest_path(map_40, 5, 34)\n", + "if path == [5, 16, 37, 12, 34]:\n", + " print(\"great! Your code works for these inputs!\")\n", + "else:\n", + " print(\"something is off, your code produced the following:\")\n", + " print(path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Testing your Code\n", + "If the code below produces no errors, your algorithm is behaving correctly. You are almost ready to submit! Before you submit, go through the following submission checklist:\n", + "\n", + "**Submission Checklist**\n", + "\n", + "1. Does my code pass all tests?\n", + "2. Does my code implement `A*` search and not some other search algorithm?\n", + "3. Do I use an **admissible heuristic** to direct search efforts towards the goal?\n", + "4. Do I use data structures which avoid unnecessarily slow lookups?\n", + "\n", + "When you can answer \"yes\" to all of these questions, submit by pressing the Submit button in the lower right!" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All tests pass! Congratulations!\n" + ] + } + ], + "source": [ + "from test import test\n", + "\n", + "test(shortest_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/projects/route_planner/requirements.txt b/projects/route_planner/requirements.txt new file mode 100644 index 0000000..01d16bc --- /dev/null +++ b/projects/route_planner/requirements.txt @@ -0,0 +1,2 @@ +networkx==1.11 +plotly==2.0.15 diff --git a/projects/route_planner/student_code.py b/projects/route_planner/student_code.py new file mode 100644 index 0000000..4cf9ea8 --- /dev/null +++ b/projects/route_planner/student_code.py @@ -0,0 +1,230 @@ +import math + + +class GraphNode: + """A node in a graph. It has an id, x and y coordinates, and a list of + nodes it is connected to. + """ + + def __init__(self, node_id, x, y): + self.id = node_id + self.x = x + self.y = y + self.reset() + + def reset(self): + """Reset the node to its initial state.""" + self.via = None + self.visited = False + self.distance_to_goal = float("inf") + self.distance_to_origin = float("inf") + + @property + def total_distance_to_goal(self): + """The total distance to the goal. It is the sum of the distance to + the origin and the distance to the goal.""" + return self.distance_to_origin + self.distance_to_goal + + +class PriorityQueue: + """A priority queue that stores nodes and their total distances. It + allows adding or updating a node, and removing the node with the + smallest total distance. + """ + + def __init__(self): + self.nodes = {} + + def add_or_update(self, node_id, total_distance): + """Add a node to the queue, or update its total distance if it is + already in the queue. + + Args: + node_id: The id of the node. + total_distance: The total distance to the node. + """ + self.nodes[node_id] = total_distance + + def remove(self): + """Remove the node with the smallest total distance from the queue. + + Returns: + The id of the node with the smallest total distance, or None if + the queue is empty. + """ + if not self.nodes: + return None + + node_id = min(self.nodes, key=self.nodes.get) + del self.nodes[node_id] + return node_id + + +class GraphSpace: + """A graph space that contains nodes and lines. It allows finding + linked nodes for a given node. + """ + + def __init__(self, nodes, lines): + self.nodes = { + node_id: GraphNode(node_id, pos[0], pos[1]) + for node_id, pos in nodes.items() + } + self.lines = {line_id: nodes for line_id, nodes in enumerate(lines)} + + def find_linked_nodes(self, node_id): + """Find the ids of nodes that are linked to the given node. + + Args: + node_id: The id of the node. + + Returns: + A list of ids of nodes that are linked to the given node. + """ + return self.lines.get(node_id, []) + + +class PathFinder: + """A path finder that finds the shortest path between two nodes in a + graph space. + """ + + def __init__(self, space): + self.space = space + self.frontier = PriorityQueue() + self.goal = None + self.origin = None + + def calculate_distance(self, from_xy, to_xy): + """Calculate the distance between two points. + + Args: + from_xy: The x and y coordinates of the first point. + to_xy: The x and y coordinates of the second point. + + Returns: + The distance between the two points. + """ + diff_x = math.fabs(from_xy[0] - to_xy[0]) + diff_y = math.fabs(from_xy[1] - to_xy[1]) + return math.sqrt(diff_x**2 + diff_y**2) + + def initialize_goal(self, goal_id): + """Initialize the goal node. + + Args: + goal_id: The id of the goal node. + """ + self.goal = self.space.nodes[goal_id] + self.goal.distance_to_goal = 0 + + def initialize_origin(self, origin_id): + """Initialize the origin node. + + Args: + origin_id: The id of the origin node. + """ + self.origin = self.space.nodes[origin_id] + self.origin.distance_to_origin = 0 + + def add_to_frontier(self, from_node, to_node): + """Add a node to the frontier, or update its total distance if it is + already in the frontier. + + Args: + from_node: The node from which the edge to the node is. + to_node: The node to add to the frontier. + """ + if to_node.visited: + return + + distance_to_node = ( + 0 + if from_node is None + else ( + self.calculate_distance( + (from_node.x, from_node.y), (to_node.x, to_node.y) + ) + + from_node.distance_to_origin + ) + ) + + if distance_to_node < to_node.distance_to_origin: + to_node.distance_to_origin = distance_to_node + to_node.via = from_node + + if math.isinf(to_node.distance_to_goal): + to_node.distance_to_goal = ( + self.calculate_distance( + (to_node.x, to_node.y), (self.goal.x, self.goal.y) + ) + * 0.9 + ) + + self.frontier.add_or_update(to_node.id, to_node.total_distance_to_goal) + + def get_route_ids(self): + """Get the ids of the nodes in the shortest path. + + Returns: + A list of the ids of the nodes in the shortest path. + """ + route = [] + node = self.goal + while node is not None: + route.append(node.id) + node = node.via + return list(reversed(route)) + + def find_shortest_path(self, origin_id, goal_id): + """Find the shortest path between two nodes. + + Args: + origin_id: The id of the origin node. + goal_id: The id of the goal node. + + Returns: + A list of the ids of the nodes in the shortest path, or None if + no path is found. + """ + if origin_id not in self.space.nodes or goal_id not in self.space.nodes: + return None + + if origin_id == goal_id: + return [origin_id] + + self.initialize_origin(origin_id) + self.initialize_goal(goal_id) + self.add_to_frontier(None, self.origin) + + return self._find_shortest_path() + + def _find_shortest_path(self): + """Find the shortest path between two nodes. + + Returns: + A list of the ids of the nodes in the shortest path, or None if + no path is found. + """ + active_node_id = self.frontier.remove() + + if active_node_id is None: + return None + if active_node_id == self.goal.id: + return self.get_route_ids() + + active_node = self.space.nodes[active_node_id] + active_node.visited = True + + for linked_id in self.space.find_linked_nodes(active_node_id): + linked_node = self.space.nodes.get(linked_id) + if linked_node is not None: + self.add_to_frontier(active_node, linked_node) + + return self._find_shortest_path() + + +def shortest_path(map_object, start, goal): + space = GraphSpace(map_object.intersections, map_object.roads) + finder = PathFinder(space) + return finder.find_shortest_path(start, goal) diff --git a/projects/route_planner/test.py b/projects/route_planner/test.py new file mode 100644 index 0000000..1442846 --- /dev/null +++ b/projects/route_planner/test.py @@ -0,0 +1,25 @@ +from helpers import load_map + +MAP_40_ANSWERS = [ + (5, 34, [5, 16, 37, 12, 34]), + (5, 5, [5]), + (8, 24, [8, 14, 16, 37, 12, 17, 10, 24]) +] + + +def test(shortest_path_function): + map_40 = load_map('map-40.pickle') + correct = 0 + for start, goal, answer_path in MAP_40_ANSWERS: + path = shortest_path_function(map_40, start, goal) + if path == answer_path: + correct += 1 + else: + print("For start:", start, + "Goal: ", goal, + "Your path:", path, + "Correct: ", answer_path) + if correct == len(MAP_40_ANSWERS): + print("All tests pass! Congratulations!") + else: + print("You passed", correct, "/", len(MAP_40_ANSWERS), "test cases") diff --git a/projects/show_me_the_data_structures/README.md b/projects/show_me_the_data_structures/README.md new file mode 100644 index 0000000..c6fe7af --- /dev/null +++ b/projects/show_me_the_data_structures/README.md @@ -0,0 +1,46 @@ +# Show me the data structures + +## Data Structure Questions + +For this project, you will answer the six questions laid out in the next sections. The questions cover a variety of topics related to the data structures you've learned in this course. You will write up a clean and efficient answer in Python, as well as a text explanation of the efficiency of your code and your design choices. + +## Problem 1: Least Recently Used Cache + +A Least Recently Used (LRU) Cache is a type of cache in which we remove the least recently used entry when the cache memory reaches its limit. For the current problem, consider both get and set operations as an use operation. + +Your job is to use an appropriate data structure to implement a LRU cache which has the following methods: + +- `set(key, value)`: set the value if the key is not present in the cache. If the cache is at capacity, remove the oldest entry. +- `get(key)`: return the value if the key is present in the cache, otherwise return -1. + +Code in `problem_1.py` and explanation in `explanation_problem_1.md` + +## Problem 2: File Recursion + +For this problem, the goal is to write code for finding all files under a directory (and all directories beneath it) that end with ".c" + +Code in `problem_2.py` and explanation in `explanation_problem_2.md` + +## Problem 3: Huffman Coding + +Huffman coding is a compression algorithm that can be used to compress lists of characters. The algorithm uses a binary tree to assign variable-length codes to each character. The more frequent the character, the shorter the code used to represent it. The binary tree is constructed in such a way that the code assigned to each character is the prefix of any other code assigned to any other character. + +Code in `problem_3.py` and explanation in `explanation_problem_3.md` + +## Problem 4: Active Directory + +In Windows Active Directory, a group can consist of user(s) and group(s) themselves. We can construct this hierarchy as such. Where User is represented by str representing their ids. + +Code in `problem_4.py` and explanation in `explanation_problem_4.md` + +## Problem 5: Blockchain + +A Blockchain is a sequential chain of records, similar to a linked list. Each block contains some information and how it is connected related to the other blocks in the chain. Each block contains a cryptographic hash of the previous block, a timestamp, and transaction data. For our blockchain, we will be using a SHA-256 hash, the Greenwich Mean Time when the block was created, and text strings as the data. + +Code in `problem_5.py` and explanation in `explanation_problem_5.md` + +## Problem 6: Union and Intersection of Two Linked Lists + +Your task for this problem is to fill out the union and intersection functions. The union of two sets A and B is the set of elements which are in A, in B, or in both A and B. The intersection of two sets A and B, denoted by A ∩ B, is the set of all objects that are members of both the sets A and B. + +Code in `problem_6.py` and explanation in `explanation_problem_6.md` diff --git a/projects/show_me_the_data_structures/explanation_problem1.md b/projects/show_me_the_data_structures/explanation_problem1.md new file mode 100644 index 0000000..a1d0e89 --- /dev/null +++ b/projects/show_me_the_data_structures/explanation_problem1.md @@ -0,0 +1,10 @@ +# Least Recently Used (LRU) Cache + +Time Complexity: + +- `get(key)`: The time complexity is O(1) because it uses a dictionary (hashmap) to retrieve the value associated with a key. +- `set(key, value)`: The time complexity is O(1) for setting a new key-value pair in the dictionary. However, when the capacity of the cache is exceeded, the oldest item (first item in the dictionary) is removed, which takes O(n) time because it involves creating a list of keys and accessing the first one. + +Space Complexity: The space complexity is O(n), where n is the capacity of the cache. This is because a dictionary is used to store the key-value pairs, and the size of the dictionary is at most the capacity of the cache. + +However, the current implementation does not fully satisfy the requirements of an LRU Cache. In an LRU Cache, the get operation should also update the "recently used" status of the key. The set operation, when the capacity is exceeded, should remove the least recently used item, not just the oldest item. To achieve this, a combination of a doubly linked list (to keep track of the order of usage) and a dictionary (for O(1) access) is typically used. This would still maintain the O(1) time complexity for get and set operations, and O(n) space complexity. diff --git a/projects/show_me_the_data_structures/explanation_problem2.md b/projects/show_me_the_data_structures/explanation_problem2.md new file mode 100644 index 0000000..ba63fcd --- /dev/null +++ b/projects/show_me_the_data_structures/explanation_problem2.md @@ -0,0 +1,5 @@ +# Finds all files with a given suffix in a specified directory and its subdirectories. + +Time Complexity: The time complexity of the function is O(n), where n is the total number of files and directories in the specified path. This is because the function uses os.walk(), which generates the file names in a directory tree by walking the tree either top-down or bottom-up. For each directory in the tree rooted at directory top (including top itself), it produces a 3-tuple (dirpath, dirnames, filenames). The function then checks each file name to see if it ends with the specified suffix. + +Space Complexity: The space complexity of the function is also O(n), where n is the total number of files and directories in the specified path. This is because os.walk() generates a list of 3-tuples, and the function creates a list of file paths. In the worst-case scenario (every file has the specified suffix), the list of file paths will have as many elements as there are files. diff --git a/projects/show_me_the_data_structures/explanation_problem3.md b/projects/show_me_the_data_structures/explanation_problem3.md new file mode 100644 index 0000000..185514f --- /dev/null +++ b/projects/show_me_the_data_structures/explanation_problem3.md @@ -0,0 +1,11 @@ +# Huffman Encoding and Decoding + +Time Complexity: + +- `huffman_encoding(data)`: The time complexity is O(n log n), where n is the number of unique characters in the data. This is because the function uses a priority queue (implemented as a heap) to build the Huffman tree. Inserting an element into a heap takes O(log n) time, and we do this for each unique character. +- `huffman_decoding(data, tree)`: The time complexity is O(m), where m is the length of the encoded data. This is because we traverse the Huffman tree for each bit in the encoded data. + +Space Complexity: + +- `huffman_encoding(data)`: The space complexity is O(n), where n is the number of unique characters in the data. This is because we create a node for each unique character and store these nodes in a heap. In addition, we create a dictionary to store the Huffman codes. +- `huffman_decoding(data, tree)`: The space complexity is O(n), where n is the number of unique characters in the data. This is because we need to store the Huffman tree. The decoded data also takes up space, but its size is equivalent to the original data, so it doesn't increase the space complexity. diff --git a/projects/show_me_the_data_structures/explanation_problem4.md b/projects/show_me_the_data_structures/explanation_problem4.md new file mode 100644 index 0000000..9bbc1dd --- /dev/null +++ b/projects/show_me_the_data_structures/explanation_problem4.md @@ -0,0 +1,5 @@ +# Group that represents a group of users + +Time Complexity: The time complexity of the is_user_in_group(user, group) function is O(n), where n is the total number of users and groups. This is because in the worst-case scenario, the function needs to check each user in each group and subgroup. The function uses recursion to traverse all groups and subgroups. + +Space Complexity: The space complexity of the is_user_in_group(user, group) function is also O(n), where n is the total number of groups. This is because in the worst-case scenario, the function will have to recurse through each group and subgroup, which will add to the call stack. The depth of the recursion equals the depth of the group hierarchy, which in the worst case can be as large as the number of groups. diff --git a/projects/show_me_the_data_structures/explanation_problem5.md b/projects/show_me_the_data_structures/explanation_problem5.md new file mode 100644 index 0000000..22e69e5 --- /dev/null +++ b/projects/show_me_the_data_structures/explanation_problem5.md @@ -0,0 +1,11 @@ +# blockchain with the ability to append new blocks + +Time Complexity: + +- `Block.__init__(self, timestamp, data, previous_hash)`: The time complexity is O(1) because it only involves assigning values to instance variables and calculating a hash, which is a constant-time operation for fixed-size inputs. +- `BlockChain.append(self, data)`: The time complexity is O(1) because it involves creating a new block and appending it to the list of blocks, both of which are constant-time operations. + +Space Complexity: + +- `Block.__init__(self, timestamp, data, previous_hash)`: The space complexity is O(1) because it only involves storing a fixed number of instance variables. +- `BlockChain.append(self, data)`: The space complexity is O(n), where n is the number of blocks in the blockchain. This is because each block is stored in a list, and the size of the list grows linearly with the number of blocks. diff --git a/projects/show_me_the_data_structures/explanation_problem6.md b/projects/show_me_the_data_structures/explanation_problem6.md new file mode 100644 index 0000000..c44385c --- /dev/null +++ b/projects/show_me_the_data_structures/explanation_problem6.md @@ -0,0 +1,14 @@ +# inked list and provides functions to find the union and intersection of two linked lists + +Time Complexity: + +- `LinkedList.append(value)`: The time complexity is O(n), where n is the number of nodes in the linked list. This is because in the worst-case scenario, the function needs to traverse the entire list to find the last node. +- `LinkedList.size()`: The time complexity is O(n), where n is the number of nodes in the linked list. This is because the function needs to traverse the entire list to count the nodes. +- `convert_to_set(linked_list)`: The time complexity is O(n), where n is the number of nodes in the linked list. This is because the function needs to traverse the entire list to add each node to the set. +- `union(list_1, list_2)` and `intersection(list_1, list_2)`: The time complexity is O(n + m), where n and m are the number of nodes in list_1 and list_2, respectively. This is because the functions need to convert each list to a set and then iterate over the sets. + +Space Complexity: + +- `LinkedList.append(value) and LinkedList.size()`: The space complexity is O(1) because these functions only use a constant amount of space to store temporary variables. +- `convert_to_set(linked_list)`: The space complexity is O(n), where n is the number of nodes in the linked list. This is because the function creates a set that contains all the nodes in the list. +- `union(list_1, list_2)` and `intersection(list_1, list_2)`: The space complexity is O(n + m), where n and m are the number of nodes in list_1 and list_2, respectively. This is because the functions create a new linked list to store the result, and the size of the result can be up to n + m nodes. diff --git a/projects/show_me_the_data_structures/problem1.py b/projects/show_me_the_data_structures/problem1.py new file mode 100644 index 0000000..6b7eb06 --- /dev/null +++ b/projects/show_me_the_data_structures/problem1.py @@ -0,0 +1,74 @@ +""" +Course: Data Structures & Algorithms +Core: Data Structures +Problem: 1 +Code by: KhoiVN +Date: 04/01/2024 +""" + + +class LRUCache(object): + + def __init__(self, capacity): + # Initialize class variables + if not isinstance(capacity, int) or capacity < 0: + self.capacity = 0 + else: + self.capacity = capacity + self.lru_cache = {} + + def __repr__(self): + return f"LRUCache({self.lru_cache})" + + def get(self, key): + return self.lru_cache.get(key, -1) + + def set(self, key, value): + self.lru_cache[key] = value + if len(self.lru_cache) > self.capacity and self.capacity != 0: + self.lru_cache.pop(list(self.lru_cache.keys())[0]) + + +our_cache = LRUCache(5) + +our_cache.set(1, 1) +our_cache.set(2, 2) +our_cache.set(3, 3) +our_cache.set(4, 4) + +print(our_cache) + +# Test Case 1 +print("Test Case 1", our_cache) +assert our_cache.get(1) == 1 +assert our_cache.get(2) == 2 +assert our_cache.get(9) == -1 + +our_cache.set(5, 5) +our_cache.set(6, 6) + +# Test Case 2 +print("Test Case 2", our_cache) +assert our_cache.get(4) == 4 + +# Test Case 3 +print("Test Case 3", our_cache) +assert our_cache.get(1) == -1 +assert our_cache.get(3) == 3 +assert our_cache.get(100) == -1 + +# Edge Test Case 1 +our_cache = LRUCache(0) +print("Edge Test Case 1.1", our_cache) +assert our_cache.get(1) == -1 + +our_cache = LRUCache(100) +print("Edge Test Case 1.2", our_cache) +assert our_cache.get(1) == -1 + +# Edge Case 2 +our_cache = LRUCache(None) +print("Edge Test Case 2", our_cache) +assert our_cache.get(1) == -1 + +print("All test cases pass") diff --git a/projects/show_me_the_data_structures/problem2.py b/projects/show_me_the_data_structures/problem2.py new file mode 100644 index 0000000..4ffafa1 --- /dev/null +++ b/projects/show_me_the_data_structures/problem2.py @@ -0,0 +1,73 @@ +""" +Course: Data Structures & Algorithms +Core: Data Structures +Problem: 2 +Code by: KhoiVN +Date: 04/01/2024 +""" + +import os + + +def find_files(suffix, path): + """ + Find all files beneath path with file name suffix. + + Note that a path may contain further subdirectories + and those subdirectories may also contain further subdirectories. + + There are no limit to the depth of the subdirectories can be. + + Args: + suffix(str): suffix if the file name to be found + path(str): path of the file system + + Returns: + suffix_path_list(list): a list of paths + """ + suffix_path_list = [] + + if not os.path.isdir(path): + return suffix_path_list + + list_walk_directory = list(os.walk(path)) + + for directory in list_walk_directory: + for file in directory[2]: + if file.endswith(suffix): + suffix_path_list.append(os.path.join(directory[0], file)) + return suffix_path_list + + +# Test Case 1 +test_case_1 = find_files(".c", "./testdir") +assert sorted(test_case_1) == sorted( + [ + "./testdir/subdir1/a.c", + "./testdir/subdir3/subsubdir1/b.c", + "./testdir/t1.c", + "./testdir/subdir5/a.c", + ] +), f"Test Case 1 Failed, expected {test_case_1}" + +# Test Case 2 +test_case_2 = find_files(".c", "./testdir/subdir1") +assert sorted(test_case_2) == sorted( + ["./testdir/subdir1/a.c"] +), f"Test Case 2 Failed, expected {test_case_2}" + +# Test Case 3 +test_case_3 = find_files(".c", "./testdir/subdir3/subsubdir1") +assert sorted(test_case_3) == sorted( + ["./testdir/subdir3/subsubdir1/b.c"] +), f"Test Case 3 Failed, expected {test_case_3}" + +# Edge Case 1 +edge_case_1 = find_files(".", "./testdir/subdir5") +assert sorted(edge_case_1) == [], f"Edge Case 1 Failed, expected {edge_case_1}" + +# Edge Case 2 +edge_case_2 = find_files("c", "./testdir/s") +assert sorted(edge_case_2) == [], f"Edge Case 2 Failed, expected {edge_case_2}" + +print("All test cases passed!") diff --git a/projects/show_me_the_data_structures/problem3.py b/projects/show_me_the_data_structures/problem3.py new file mode 100644 index 0000000..cec3a82 --- /dev/null +++ b/projects/show_me_the_data_structures/problem3.py @@ -0,0 +1,131 @@ +""" +Course: Data Structures & Algorithms +Core: Data Structures +Problem: 3 +Code by: KhoiVN +Date: 04/01/2024 +""" + +import sys +import heapq +from collections import Counter + + +class Node: + def __init__(self, value=None, freq=0, left=None, right=None): + self.value = value + self.freq = freq + self.left = left + self.right = right + + def __lt__(self, other): + return self.freq < other.freq + + def build_code(self, prefix="", code={}): + if self.value: + code[self.value] = prefix + if self.left: + self.left.build_code(prefix + "0", code) + if self.right: + self.right.build_code(prefix + "1", code) + return code + + def encode_data(self, data): + return "".join([self.build_code()[char] for char in data]) + + def __repr__(self) -> str: + return f"Node({self.value}, {self.freq})" + + +def huffman_encoding(data="AAAAAAABBBCCCCCCCDDEEEEEE"): + if not data or len(data) == 0: + return "", None + counter = Counter(data) + if len(counter) == 1: + return "0" * len(data), Node(None, len(data)) + list_node = [Node(value, freq) for value, freq in counter.items()] + heapq.heapify(list_node) + + while len(list_node) > 1: + left = heapq.heappop(list_node) + right = heapq.heappop(list_node) + parent = Node(None, left.freq + right.freq, left, right) + heapq.heappush(list_node, parent) + + tree = list_node[0] + code = tree.encode_data(data) + + return code, tree + + +def huffman_decoding(data, tree): + result = "" + node = tree + for bit in data: + if bit == "0": + node = node.left + else: + node = node.right + if node.value: + result += node.value + node = tree + return result + + +code, tree = huffman_encoding("AAAAAAABBBCCCCCCCDDEEEEEE") + +# Test Case 1 +assert ( + code == "1010101010101000100100111111111111111000000010101010101" +), f"Test Case 1 Failed, expected {code}" +assert tree.value is None, f"Test Case 1 Failed, expected {tree.value}" + + +result = huffman_decoding(code, tree) +# Test Case 2 +assert result == "AAAAAAABBBCCCCCCCDDEEEEEE", f"Test Case 2 Failed, expected {result}" +assert len(result) == 25, f"Test Case 2 Failed, expected {len(result)}" + +# Test Case 3 +assert huffman_encoding("") == ( + "", None), "Test Case 3 Failed, expected ('', None)" +assert huffman_decoding("", None) == "", "Test Case 3 Failed, expected ''" + + +# Edge Case 1 +assert huffman_encoding(None) == ( + "", None), "Edge Case 1 Failed, expected ('', None)" +assert huffman_decoding("", None) == "", "Edge Case 1 Failed, expected ''" + +# Edge Case 2 +assert huffman_encoding("A" * 10)[0] == "0" * \ + 10, "Edge Case 2 Failed, expected '0'*10" +assert huffman_decoding("0" * 0, Node(None, 0) + ) == "", "Edge Case 2 Failed, expected ''" + +print("All test cases passed!") + +if __name__ == "__main__": + codes = {} + + a_great_sentence = "The bird is the word" + + print("The size of the data is: {}\n".format( + sys.getsizeof(a_great_sentence))) + print("The content of the data is: {}\n".format(a_great_sentence)) + + encoded_data, tree = huffman_encoding(a_great_sentence) + + print( + "The size of the encoded data is: {}\n".format( + sys.getsizeof(int(encoded_data, base=2)) + ) + ) + print("The content of the encoded data is: {}\n".format(encoded_data)) + + decoded_data = huffman_decoding(encoded_data, tree) + + print( + "The size of the decoded data is: {}\n".format( + sys.getsizeof(decoded_data))) + print("The content of the encoded data is: {}\n".format(decoded_data)) diff --git a/projects/show_me_the_data_structures/problem4.py b/projects/show_me_the_data_structures/problem4.py new file mode 100644 index 0000000..8dc4025 --- /dev/null +++ b/projects/show_me_the_data_structures/problem4.py @@ -0,0 +1,80 @@ +""" +Course: Data Structures & Algorithms +Core: Data Structures +Problem: 4 +Code by: KhoiVN +Date: 05/01/2024 +""" + + +class Group(object): + def __init__(self, _name): + self.name = _name + self.groups = [] + self.users = [] + + def add_group(self, group): + self.groups.append(group) + + def add_user(self, user): + self.users.append(user) + + def get_groups(self): + return self.groups + + def get_users(self): + return self.users + + def get_name(self): + return self.name + + +parent = Group("parent") +child = Group("child") +sub_child = Group("subchild") + +sub_child_user = "sub_child_user" +sub_child.add_user(sub_child_user) + +child.add_group(sub_child) +parent.add_group(child) + + +def is_user_in_group(user, group): + """ + Return True if user is in the group, False otherwise. + + Args: + user(str): user name/id + group(class:Group): group to check user membership against + """ + if not isinstance(group, Group): + return False + if user in group.get_users(): + return True + for sub_group in group.get_groups(): + if is_user_in_group(user, sub_group): + return True + return False + + +# Test Case 1 +assert is_user_in_group(sub_child_user, parent), "Test Case 1 Failed" +assert is_user_in_group(sub_child_user, child), "Test Case 1 Failed" + +# Test Case 2 +assert is_user_in_group(None, parent) == False, "Test Case 2 Failed" +assert is_user_in_group("", parent) == False, "Test Case 2 Failed" + +# Test Case 3 +assert is_user_in_group("sub_child_user", parent), "Test Case 3 Failed" +assert is_user_in_group("sub_child_user", sub_child), "Test Case 3 Failed" + +# Edge Case 1 +assert is_user_in_group("sub_child_user", None) == False, "Edge Case 1 Failed" +assert is_user_in_group("sub_child_user", "") == False, "Edge Case 1 Failed" + +# Edge Case 2 +assert is_user_in_group("sub_child_user", parent), "Edge Case 2 Failed" + +print("All test cases passed!") diff --git a/projects/show_me_the_data_structures/problem5.py b/projects/show_me_the_data_structures/problem5.py new file mode 100644 index 0000000..9bd2318 --- /dev/null +++ b/projects/show_me_the_data_structures/problem5.py @@ -0,0 +1,115 @@ +""" +Course: Data Structures & Algorithms +Core: Data Structures +Problem: 5 +Code by: KhoiVN +Date: 05/01/2024 +""" + +import hashlib +import time + + +class Block: + + def __init__(self, timestamp, data, previous_hash): + self.timestamp = timestamp + self.data = data + self.previous_hash = previous_hash + self.hash = self.calc_hash() + + def calc_hash(self): + sha = hashlib.sha256() + hash_str = self.data.encode("utf-8") + sha.update(hash_str) + return sha.hexdigest() + + def __repr__(self): + return ( + f"Block({self.timestamp}, {self.data}, {self.previous_hash}, {self.hash})" + ) + + +class BlockChain: + def __init__(self): + self.head = None + self.tail = None + self.block = [] + + def append(self, data): + if self.block: + previous_hash = self.block[-1].hash + else: + previous_hash = None + + block = Block(time.time(), data, previous_hash) + self.block.append(block) + + def __repr__(self): + return f"BlockChain({self.block})" + + +# Add your own test cases: include at least three test cases +# and two of them must include edge cases, such as null, empty or very +# large values + +block_chain = BlockChain() +block_chain.append("Algorithms") +block_chain.append("Data Structures") +block_chain.append("KhoiVN") + +# Test Case 1 +assert ( + block_chain.block[0].data == "Algorithms" +), f"Test Case 1 Failed, expected {block_chain.block[0].data}" +assert ( + block_chain.block[1].data == "Data Structures" +), f"Test Case 1 Failed, expected {block_chain.block[1].data}" +assert ( + block_chain.block[2].data == "KhoiVN" +), f"Test Case 1 Failed, expected {block_chain.block[2].data}" + +# Test Case 2 +assert ( + block_chain.block[0].previous_hash is None +), f"Test Case 2 Failed, expected {block_chain.block[0].previous_hash}" +assert ( + block_chain.block[1].previous_hash + == "3c34d938281ebe9c3c41c91c11178d9527cd460911135a01f553bcc9b4fc859d" +), f"Test Case 2 Failed, expected {block_chain.block[1].previous_hash}" +assert ( + block_chain.block[2].previous_hash + == "21692e8647432d796d4aa5d60544feb7c120954721795c19adebfbc8f13516bb" +), f"Test Case 2 Failed, expected {block_chain.block[2].previous_hash}" + +# Test Case 3 +assert ( + block_chain.block[0].timestamp < block_chain.block[1].timestamp +), f"Test Case 3 Failed, expected {block_chain.block[0].timestamp} < {block_chain.block[1].timestamp}" +assert ( + block_chain.block[1].timestamp < block_chain.block[2].timestamp +), f"Test Case 3 Failed, expected {block_chain.block[1].timestamp} < {block_chain.block[2].timestamp}" + +# Edge Test Case 1 +block_chain = BlockChain() +block_chain.append("") +block_chain.append("") +assert ( + block_chain.block[0].data == "" +), f"Edge Test Case 1 Failed, expected {block_chain.block[0].data}" +assert ( + block_chain.block[1].data == "" +), f"Edge Test Case 1 Failed, expected {block_chain.block[1].data}" + +# Edge Test Case 2 +block_chain = BlockChain() +block_chain.append("A" * 10**6) +block_chain.append("B" * 10**6) +assert ( + block_chain.block[0].data == "A" * 10**6 +), f"Edge Test Case 2 Failed, expected {block_chain.block[0].data}" +assert ( + block_chain.block[1].data == "B" * 10**6 +), f"Edge Test Case 2 Failed, expected {block_chain.block[1].data}" + +print("All test cases passed!") diff --git a/projects/show_me_the_data_structures/problem6.py b/projects/show_me_the_data_structures/problem6.py new file mode 100644 index 0000000..800e2d5 --- /dev/null +++ b/projects/show_me_the_data_structures/problem6.py @@ -0,0 +1,158 @@ +""" +Course: Data Structures & Algorithms +Core: Data Structures +Problem: 6 +Code by: KhoiVN +Date: 05/01/2024 +""" + + +class Node: + def __init__(self, value): + self.value = value + self.next = None + + def __repr__(self): + return str(self.value) + + +class LinkedList: + def __init__(self): + self.head = None + + def __str__(self): + cur_head = self.head + out_string = "" + while cur_head: + out_string += str(cur_head.value) + " -> " + cur_head = cur_head.next + return out_string + + def append(self, value): + + if self.head is None: + self.head = Node(value) + return + + node = self.head + while node.next: + node = node.next + + node.next = Node(value) + + def size(self): + size = 0 + node = self.head + while node: + size += 1 + node = node.next + + return size + + +def convert_to_set(linked_list): + current = linked_list.head + node_set = set() + + while current: + node_set.add(current.value) + current = current.next + + return node_set + + +def union(list_1, list_2): + linked_list = LinkedList() + list_1 = convert_to_set(list_1) + list_2 = convert_to_set(list_2) + for item in list_1: + linked_list.append(item) + for item in list_2: + linked_list.append(item) + return linked_list + + +def intersection(list_1, list_2): + linked_list = LinkedList() + list_1 = convert_to_set(list_1) + list_2 = convert_to_set(list_2) + for item in list_1: + if item in list_2: + linked_list.append(item) + return linked_list + + +# Test case 1 + +linked_list_1 = LinkedList() +linked_list_2 = LinkedList() + +element_1 = [3, 2, 4, 35, 6, 65, 6, 4, 3, 21] +element_2 = [6, 32, 4, 9, 6, 1, 11, 21, 1] + +for i in element_1: + linked_list_1.append(i) + +for i in element_2: + linked_list_2.append(i) + +print(union(linked_list_1, linked_list_2)) +print(intersection(linked_list_1, linked_list_2)) + +# Test case 2 + +linked_list_3 = LinkedList() +linked_list_4 = LinkedList() + +element_1 = [3, 2, 4, 35, 6, 65, 6, 4, 3, 23] +element_2 = [1, 7, 8, 9, 11, 21, 1] + +for i in element_1: + linked_list_3.append(i) + +for i in element_2: + linked_list_4.append(i) + +print(union(linked_list_3, linked_list_4)) +print(intersection(linked_list_3, linked_list_4)) + + +# Test Case 3 +assert union(linked_list_1, linked_list_2).size() == 14 +assert union(linked_list_3, linked_list_4).size() == 13 +assert intersection(linked_list_1, linked_list_2).size() == 3 +assert intersection(linked_list_3, linked_list_4).size() == 0 + +# Edge Case 1 +linked_list_5 = LinkedList() +linked_list_6 = LinkedList() + +element_1 = [3, 2, 4, 35, 6, 65, 6, 4, 3, 21] +element_2 = [] + +for i in element_1: + linked_list_5.append(i) + +for i in element_2: + linked_list_6.append(i) + +assert union(linked_list_5, linked_list_6).size() == 7 +assert intersection(linked_list_5, linked_list_6).size() == 0 + +# Edge Case 2 +linked_list_7 = LinkedList() +linked_list_8 = LinkedList() + +element_1 = [] +element_2 = [] + +for i in element_1: + linked_list_7.append(i) + +for i in element_2: + linked_list_8.append(i) + +assert union(linked_list_7, linked_list_8).size() == 0 +assert intersection(linked_list_7, linked_list_8).size() == 0 + +print("All test cases passed!") diff --git a/projects/show_me_the_data_structures/testdir/subdir1/a.c b/projects/show_me_the_data_structures/testdir/subdir1/a.c new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/subdir1/a.h b/projects/show_me_the_data_structures/testdir/subdir1/a.h new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/subdir2/.gitkeep b/projects/show_me_the_data_structures/testdir/subdir2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/subdir3/subsubdir1/b.c b/projects/show_me_the_data_structures/testdir/subdir3/subsubdir1/b.c new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/subdir3/subsubdir1/b.h b/projects/show_me_the_data_structures/testdir/subdir3/subsubdir1/b.h new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/subdir4/.gitkeep b/projects/show_me_the_data_structures/testdir/subdir4/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/subdir5/a.c b/projects/show_me_the_data_structures/testdir/subdir5/a.c new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/subdir5/a.h b/projects/show_me_the_data_structures/testdir/subdir5/a.h new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/t1.c b/projects/show_me_the_data_structures/testdir/t1.c new file mode 100644 index 0000000..e69de29 diff --git a/projects/show_me_the_data_structures/testdir/t1.h b/projects/show_me_the_data_structures/testdir/t1.h new file mode 100644 index 0000000..e69de29 diff --git a/projects/unscramble_computer_science_problems/Analysis.txt b/projects/unscramble_computer_science_problems/Analysis.txt new file mode 100644 index 0000000..b25f91f --- /dev/null +++ b/projects/unscramble_computer_science_problems/Analysis.txt @@ -0,0 +1,73 @@ +# Analysis Big O Notation + +Read calls.txt and texts.txt required O(n) time to read the file and store the data into a list. + +Task 0: +Prints the first and last element of a list of size n + +> O(n) - Linear Time + +Task 1: +For each element in a list of size n, add into set if not already in set. Then print the set. +Creating the set of unique telephone numbers: The for loop iterates over each text and call, which is 2n iterations in total. +For each iteration, it performs four add operations to the set. +The add operation in a set in Python is O(1). +Therefore, the total complexity of this part is O(2n) * O(1) = O(n). + +> O(n) - Linear Time + +Task 2: + +1.Sorting the calls: +The sorted function in Python uses a sorting algorithm called Timsort, which has a worst-case and average time complexity of O(n log n). +Therefore, the complexity of this part is O(n log n). + +2.Printing the call with the longest duration: +This operation is O(1) because it's a single operation that doesn't depend on the size of the input. + +> O(n log n) - Linearithmic Time + +Task 3: + +1.Creating the set of Bangalore calls: +The first for loop iterates over each call, which is n iterations in total. +For each iteration, it performs an add operation to the set if the condition is met. +The add operation in a set in Python is O(1). +Therefore, the total complexity of this part is O(n) * O(1) = O(n). + +2.Sorting the set of Bangalore calls: +The sorted function in Python uses a sorting algorithm called Timsort, which has a worst-case and average time complexity of O(n log n). +Therefore, the complexity of this part is O(n log n). + +3.Printing the sorted Bangalore calls: +This operation is O(n) because it iterates over each number in the sorted list. + +4.Counting the calls from and to Bangalore: +The second for loop also iterates over each call, which is n iterations in total. +For each iteration, it performs a couple of condition checks and increment operations, which are O(1). +Therefore, the total complexity of this part is O(n) * O(1) = O(n). + +5.Calculating and printing the percentage: +These operations are O(1) because they are single operations that don't depend on the size of the input. + +> O(n log n) - Linearithmic Time + +Task 4: + +Creating the sets of possible telemarketers and avoid telemarketers: +The for loops iterate over each call and text, which is 2n iterations in total. +For each iteration, it performs an add operation to the set if the condition is met. +The add operation in a set in Python is O(1). +Therefore, the total complexity of this part is O(2n) * O(1) = O(n). + +Subtracting the sets of avoid telemarketers from possible telemarketers: +The subtraction operation in a set in Python is O(len(possible_telemarketers)). In the worst case, this is O(n). + +Sorting the set of telemarketers: +The sorted function in Python uses a sorting algorithm called Timsort, which has a worst-case and average time complexity of O(n log n). +Therefore, the complexity of this part is O(n log n). + +Printing the sorted telemarketers: +This operation is O(n) because it iterates over each number in the sorted list. + +> O(n log n) - Linearithmic Time diff --git a/projects/unscramble_computer_science_problems/README.md b/projects/unscramble_computer_science_problems/README.md new file mode 100644 index 0000000..353c5dd --- /dev/null +++ b/projects/unscramble_computer_science_problems/README.md @@ -0,0 +1,201 @@ +# Investigating Texts and Calls + +## Project Overview + +In this project, you will complete five tasks based on a fabricated set of calls and texts exchanged during September 2016. You will use Python to analyze and answer questions about the texts and calls contained in the dataset. Lastly, you will perform run time analysis of your solution and determine its efficiency. + +## About the data + +The text and call data are provided in csv files. +The text data `(text.csv)` has the following columns: sending telephone number (string), receiving telephone number (string), timestamp of text message (string). +The call data `(call.csv)` has the following columns: calling telephone number (string), receiving telephone number (string), start timestamp of telephone call (string), duration of telephone call in seconds (string) + +All telephone numbers are 10 or 11 numerical digits long. Each telephone number starts with a code indicating the location and/or type of the telephone number. There are three different kinds of telephone numbers, each with a different format: + +- Fixed lines start with an area code enclosed in brackets. The area codes vary in length but always begin with 0. Example: "(022)40840621". +- Mobile numbers have no parentheses, but have a space in the middle of the number to help readability. The mobile code of a mobile number is its first four digits and they always start with 7, 8 or 9. Example: "93412 66159". +- Telemarketers' numbers have no parentheses or space, but start with the code 140. Example: "1402316533". + +## Task 0 + +What is the first record of texts and what is the last record of calls? + +>Print messages: +"First record of texts, texts at time