I successfully completed all the readings, lectures, homework assignments, and projects for MIT’s Introduction to Algorithms (the fall 2011 version of 6.006) course. I’ve included examples and brief explanations of my work below to give an idea of the types of things I’ve learned. All of my programming work and my final solutions to the homework assignments can be found on my GitHub.
I’ve included some examples of my work below:
Code I wrote for content aware resizing of images. The basic idea here is you can resize images without cropping or losing content. The code shows my bottom-up dynamic programing implementation of deciding which seam (vertical path from the bottom to the top, example shown in red), would be the least noticeable if removed.
This program uses data from the National Highway Planning Network to find the shortest path from a source to a destination in the form of driving directions. This is similar to how Google Maps and other navigation software products work. Here is the code I wrote to implement Dijkstra’s algorithm, which performs the search and returns the results very quickly, as one must sort though a large amount of data to find the shortest path for a cross country trip.
Here I implemented interval subsequence hashing using a modified python dictionary I wrote to quickly compare DNA sequences (represented as series of letters). Included are my results, comparing the DNA sequences of two humans, a human and a chimp, and a human and a dog.
I dramatically improved the speed of image decryption software written for a theoretical extreme multi-core chip that is limited to only performing arithmetic operations on 8-bit or 16-bit unsigned integers. I’ve shown my straight forward arithmetic operations which perform better than that prewritten asymptotically efficient algorithms when inputs are 64 digits or less. I’ve also included an example of the type of extremely sensitive data that can be decrypted using this technology.
This is my implementation of a bidirectional search which provides the fastest (smallest number of moves) solution to an arbitrarily arranged 2×2 Rubik’s Cube (pocket cube) in <5 seconds.
This is a selected example of the written portion of a problem set, and my (neatly typed up) solutions. Although it is much easier to show and understand the value of some of the software projects I worked on in this course, it’s important to demonstrate that 6.006 is a very theory heavy course, and foundational understanding is emphasized much more than practical programming skills. I enjoy theory and figuring out creative proofs to (seemingly) difficult problems was one of my favorite parts of this course.
Here is a theory heavy problem that I found to be particularly difficult. The problem assumes that you are a newly hired employee at a competitor of Facebook and asks for an algorithm for suggesting potential friends. More specifically it requests that the algorithm find the strength (computed as the product of all edge-ranks, within users that are less than k degrees of separation apart), between a given user s and every other user v to whom s is connected, in O(kE+V) time (this is a graph theory problem). This solution required the creativity to recognize that maximizing a sum (the strength) of a product (the edge ranks) is equivalent to minimizing the sum over the log of 1/(the product). This converts the problem to be a shortest path problem for all paths shorter than k in length.
What I just described is technical, but the reason I chose this problem is to demonstrate that this MIT course teaches students to think creatively and challenges students to think outside of the box. A cookie cutter application of an algorithm from our algorithms textbook doesn’t suffice. The course teaches more about how to reframe real world problems so we can map them to previously solved problems and apply the solutions, which I believe is the most valuable skill one can learn in almost any domain.
This is another example of a proof that I found to be particularly valuable, but also built on the most important skill of this course, reframing problems and modifying solutions so you can use resources of previously solved problems (in this field these are called algorithms) and apply them to novel problems. In this particular problem I am writing pseudocode to develop more basic arithmetic operations for the theoretical chip from the image decryption problem.
How I did it:
Before I analyze what I did, I’ll give a raw explanation of what I actually did. I completed this MIT Course in my free time by using an evolving version of the learning method I developed and found success with in college. The method breaks learning a college course into three phases, which are listed below, along with the actions I took to complete those phases.
I started on the first problem set, attempting each problem and subproblem individually and discretely to the best of my ability, and providing a falsifiable answer, which I then immediately compared to the official course solutions. If my solution was correct I crossed the problem number off my mega adaptive set, and neatly wrote up my solution to be used as an “official solution”. If my solution was incorrect I circled the problem on my mega adaptive set, compared my attempted solution with the official solution line-by-line, identifying where exactly I went wrong, correcting my mistakes, and completing my incorrect solution using the official solution as a guide. Then I threw away my corrected, attempted solution, and moved on to the next problem on my set. Once I reach the end of the problem set (I call each of these a run-through) I circle the problem set number if there are circled problems remaining on the problem set. I then attempt another run-though of the problem set, where I repeat the process described above, but only for the circled problems. It is important to note that I do not attempt a circled problem within 24 hours of circling (unsuccessfully attempting) it. This is to ensure that solutions are not memorized and that I’ve actually learned the important concepts and tools necessary for solving the problem. Once all the problems have been crossed off a certain problem set, I cross of the problem set number and move on to the next one. Since this is an algorithms course, I thought it would be fun to rewrite my practice set method in algorithm form.
What I learned about learning:
Selected Links for the Episode:
As a part of my software engineering course for my Graduate Unschool project, I completed the fall 2005 version of MIT’s 6.042j course, Mathematics for Computer Science. This means I read all of the course materials (textbook/lecture notes/solved problems and examples), successfully solved every problem of every homework assignment (equivalent to a perfect homework score), and synthesized the course into it’s essential elements, shown below. All of my completed solutions and my course synthesis can be found here. I’ve also included a few examples of my solutions so you can see what types of problems I solved, also below. If you want to read more about the learning process, scroll past the examples of my work.
The foundational knowledge and tools I learned/used while taking 6.042j.
How I did it:
The learning method I’ve paired with the software engineering course is essentially an evolution of the learning method I developed and used in college. It is heavily based off of the core concept of Deliberate (or possibly Directed in my case) Practice, and I am evolving it by trying new techniques I found from Scott H. Young’s learning methods, more literature on Deliberate Practice, and other experimental tweaks. Below is an overview of the phases I broke the course into. These phases were completed in discrete chunks:
There’s a lot more that goes into finishing an MIT computer science course in your free time than a just a learning method, and how I finished this course is very different than how I started because I constantly experimented with my approach and integrated what worked into my habits. I started in September 2015 after successfully completing MIT courses 6.01 and 6.02 back to back in two weeks each, with a huge plan for how I would complete all of the MIT courses on my list in just six months. I decided to experiment with taking courses simultaneously because it was what I was familiar with (traditional school), and I thought the spacing and connections would help. I’m glad I tried that because I realized that I was very wrong.
Attempting two courses at once divided my focus, and pushed the finish line further back (it should take twice as long to finish two courses if completed simultaneously) which made it more difficult to make progress because I’d experience decision fatigue when deciding what task I was going to attempt each day, and it made what was once a bite-sized achievable short term goal, completing one course, into an overwhelming project, causing me to procrastinate, fall behind, feel guilty, and become avoidant of my work.
Eventually I stopped working on Graduate Unschool completely. I went from regularly spending 6 hours a day 6 days a week making serious progress to going months without programming, forgetting where I stood in each course, and asking myself if I was ever going to follow through on my original intentions. Sporadically I’d have short intense spurts where I’d stay up all night making a new plan and plugging away at problems, but that energy would wear off, and I’d go back to my intellectual drought of pretending Graduate Unschool either didn’t exist or was a relic of my naive past.
Then one day, somewhere between a financial rock bottom and a personal career renaissance I reconnected myself with the original intentions of the Software Engineering Course. I found myself easily slipping into flow states while programming, loving it again, and wanting to get better and pursue a career in developing software, and although I didn’t have a formal education in computer science, I believed that this would fill in the gaps in my knowledge and signal to employers that I truly did “know my stuff”. I started ramping up my skills again, challenging myself everyday, and I eventually found a job as a software developer where I have been working for around seven months now. Just as I’d hoped, my job had me programming everyday, and consistently learning all sides of software development. My secondary intentions for the software course had been fulfilled, and I hadn’t even made it very far through my personal plan. I realized that it was very likely that I could continue to have a career in software without ever completing another MIT problem set or practice interview question. I also realized that I cared deeply about developing hard skills, I loved programming, and more importantly pursuing excellence and improving my programming skills as I started developing software full time. Through some introspection I found that I truly enjoyed the MIT Computer Science courses, and that completing them was important to me even after I’d successfully completed the career transition I originally thought the courses would help me with. With purified intentions, and a serious break from any disciplined self-learning routine, I made a little bit of progress everyday, built some healthy habits, finished this course, and most importantly gained insights into the learning process. My synthesis sheet clearly shows what I learned about math while taking 6.042j. Here’s what I learned about the learning process itself.
What I learned about learning:
Selected Links for the Episode:
Selected Links for the Episode:
Selected Quotes from the Episode:
Below is my 2017 bucket list, in Noah’s format, followed by a brief explanation:
To make this list I seriously reflected on: what were the most meaningful actions I took in 2016, what will I be glad I spent my time on a year (or five years) from now, what lessons do I want to learn in 2017, and what is truly important to me.
In 2017 my two main themes (what I usually use instead of goals) are: invest my time by practicing hard and important skills (play the real game and the long game), and be bad at more stuff. The majority of my ambitions are wrapped up into Graduate Unschool, most of what I want to do/learn/become is included in there, and after almost two years it is still a huge priority to me. At the end of 2016 I got in the habit of regularly dedicating time to deeply focusing on Graduate Unschool, which I’ve loved, and I believe it’s the best thing I can do to set myself up for future success, even while I still have no idea what exactly that would look like. 1000 hours is a huge amount of time, but it’s easily measurable and achievable if I make working on it a part of my life, which is the point of this goal. It should challenge me to scale up my best habits. I know that if I want to develop skills the only shortcut to putting in a massive amount of hours of deliberate practice, is putting those hours in while you’re young (which I still am), and using habit formation to minimize willpower depletion. Right now is the perfect time to internalize that lesson.
Why would someone want to spend more time being bad at things? My reason is when you’re starting something new, growing, experimenting, or learning you’re going to spend most of your time failing and being really bad at whatever you’re attempting. If I can become more comfortable shamelessly trying my best, observing objective feedback on my performance, and repeating, I’ll improve much more quickly than if I only practiced skills I’ve already mastered. The hard part about this is that I’m not shameless (yet) and my ego still convinces me that it’s embarrassing to be bad at something. Not anymore, this is the year I’m going to break this pattern. What’s actually embarrassing is being so concerned with how I’m perceived that I’m limiting my own growth. Instead I’ll be reframing poor performance and failure as an essential and important part of the learning process (and practice). In the past I’ve ended up writing less, releasing fewer podcasts, and trying fewer experiments because I’m afraid that they might turn out bad. Guess what? They probably will be bad, but they aren’t the finished product of me as a writer, a podcaster, or a student of whatever else I’m trying to learn. Part of the process of becoming a great writer is writing hundreds of bad articles (like this one) and putting them out in public so I can receive feedback and learn from them. Today I can easily prove complicated real analysis (advanced math) theorems, but years ago I had to learn how to graph functions, and solve for x just like everyone else. Those skills became easier because I solved thousands of math problems (and spent more time failing to solve problems that were outside my skill level), not because I suddenly became smarter. I never viewed elementary math as a demeaning task, so why should I feel foolish flailing my body around to learn a new skateboard trick, or making awful YouTube videos if they’re a part of the skill-building journey? The answer is simple: I shouldn’t.
2017 will be my year of practice. What will you make it into? What’s important to you this year? These are questions worth asking.
Thanks Noah.
Source of inspiration for this article: http://okdork.com/2016/12/29/how-was-your-2016/
]]>Selected Links for the Episode:
Selected Quotes from the Episode:
Selected Links for the Episode:
Selected Quotes from the Episode:
Chapters:
Selected Links for the Episode:
Selected Quotes from the Episode:
“It takes time to do stuff with your life and to get to a better place. If you’re throwing time away, you’re in trouble.”
“There’s this facade that people put on where they don’t care about anything at all” -Chanel
“If I can’t let you know that I care about you, if you get weird about that, then maybe I should care about you less.”
“I think the drive for [lack of intimacy] is ‘how can I not address the issues in my life’ by working out a lot, or sleeping with a lot of girls to numb myself.” -Chanel
“Why would I want to turn someone into a band-aid and then throw them away at the end?”
“Any excessive behavior is because you are not addressing issues that you have” -Chanel
“I would hate myself, then I would do something I regretted, and then I would feel bad about it, and then I would shame myself about it.”
“I felt the flippant nature of what life was, and it could end really soon for me, or I could get into a really crappy situation where I was gonna have to deal with medical things everyday and I was not going to be able to do anything, so I was going to do as many things as I can now and have fun and not worry about what my morality might say about it, or what my conscious is saying. I served myself… only in that time frame.” -Chanel
Notes: After reading How to Fail at Almost Everything and Still Win Big I’m testing the system of making progress on Graduate Unschool a no-exceptions daily habit. This is what made my 1% a day course work so well in the past, uninterrupted consistency, and I know once I go a few weeks without missing a day this project will require significantly less willpower.
]]>