Capstone Week 4: Problem Solving on the Shoulders of Giants
This week was very algorithm and systems design heavy. This is not much different from the way the preceding weeks have been. It’s all day, six days a week, training to analyze problems, devise solutions, optimize for space and time complexity, and architect for business values — whether those be consistency, availability, or performance as a system scales.
We have encountered and drilled on a variety of problem solving methodologies. All of these approaches have their idiosyncrasies and appropriate occasions for use. Given a variety of methods, I find myself able to solve many problems I wouldn’t have dreamt of solving not too long ago. Yet, this ostensibly impressive fact feels much less impressive than I expected. I do not feel like some veil has been lifted, nor do I feel like a super-developer who can look at a problem and understand it inside-and-out in mere seconds. Each problem is still tough; each problem still takes effort, concentration, and the ability to sit with confusion. Sometimes I still feel like I’m flying blindly into a problem with no idea how I am going to approach it. But, somehow, I usually recognize its general pattern and then I solve it. We all do… and many do it more quickly, more clearly, or more elegantly than I.
I do not feel much different with these problem solving paradigms at my fingertips. They are not magic bullets. It is akin to adding some new tools to your tool box. It doesn’t really change your ability to use any specific tool: it merely provides more options. Sometimes problem solving doesn’t seem to be much more than recognizing a problem pattern and recalling a general mental model that fits the pattern. If you see a nail, grab a hammer. If you see a screw, grab a drill.
So here is a question I find myself asking: Is a developer’s value measured by the extensiveness of their toolbox or their ability to get the job done with as few tools as possible? As I said: I could not even begin to know how to approach many of these advanced problems without the newly-added tools in my algorithmic tool box. For example, without knowledge of backtracking, there is no way I would recognize that a problem like word search could be solved by conceptualizing a decision tree, where each level is actually a level in a recursive call stack and each branch is an entry point into the next level of the stack with a different value. Backtracking is an algorithmic tool available to me that makes these sorts of problems tenable. But I’m no smarter for being able to solve this problem. I did not come up with with any of these tools: I merely use them. I am humbled by how obvious it is that I am standing on the shoulders of giants, whose hard work has made my work all the easier… and, consequently, all the less impressive.
There is a difference between “raw” problem solving talent and being able to mimic a style of thinking after repeated exposure. Despite the fact that my ability to solve problems is largely due to the ability to mimic those who are smarter than I am, I believe the problems in computer science provide an opportunity to strengthen raw problem solving talent. Although it is true that those who use problem solving paradigms far outnumber those who create them, the understanding of a paradigm varies greatly among those who use it.
You can memorize the general template for divide and conquer algorithms and then memorize key phrases in problem descriptions that indicate the appropriateness of a divide and conquer approach. This is a superficial level of understanding and it will be very difficult for someone at this level to solve more advanced, non-trivial problems that are less obvious but still amenable to a divide and conquer approach. Then there are those whose understanding of one paradigm or another is so deep that the template is nothing but a nuisance. Of course, most people start with a more “template”-like understanding and make their way to deeper levels through practice. This transition from beginner to adept is not mere mimicry — though mimicry does play a role. I believe the process of growth as a problem solver exercises something I will call “intellectual flexibility”, or the ability to think in novel and apparently unprecedented ways.
Every time we study a new “type” of problem, we have a choice about how deeply to understand that problem. This is especially the case when the problem can be solved without a deep understanding (think about the average high school math class that teaches students to memorize equations and recognize when a problem requires a particular equation… all a student must do is know which values to plug in… Yes, they may know how to take a derivative but do they understand what that really means? Do they understand why the derivative exists? Can they prove that the formulas they use will always produce the correct results, or do they take its truth at face value with no understanding of what the relationship between the input and output really is?).
When we choose to understand a paradigm deeply, we exercise intellectual flexibility, which allows us to “bend” our cognition in new ways. Intellectual flexibility is something of a meta-skill. The only way to practice is by thinking in terms of one paradigm and then thinking in terms of another paradigm. The act of switching between one way of thinking and another provides the problem solver with many different avenues of “attack” when confronting a new problem. I call this ability to “bend” one’s mind in many different ways “horizontal intellectual flexibility”. Being able to bend one’s mind in many different ways is not enough to be an adept problem solver. A superficial understanding of many different styles of thinking provides “horizontal intellectual flexibility”, but mastery requires the presence of “vertical intellectual flexibility” as well, or the presence of deep understanding of the approach or paradigm that you are choosing to mentally “bend” into at any moment.