Problem Solving Strategies: Understand the Problem
Cross-posted from The Rithm Blog.
One of the biggest challenges for the aspiring web developer has little to do with the details of programming. As an engineer, you’ll be tasked with understanding and solving problems on a regular basis; regardless of the specific technologies you use, then, strong problem-solving ability is critical.
And while your problem-solving skills will certainly develop naturally as you write code, it’s worth being a bit more intentional, and think about problem-solving strategies in general. In this series, we’ll take a look at some common problem-solving strategies, and adapt them to the life of a web developer. (Note: many of these strategies are adapted from George Polya, whose book How To Solve It is a great resource for anyone who wants to become a better problem solver.)
Since this is the first post in the series, we’ll start with the beginning of the problem-solving process: understanding the problem.
Strategy #1: Understand the Problem
When someone presents you with a problem, it can be tempting to dive right in and start solving. This is especially true in programming, since a fast typer can churn out a decent amount of code in a short amount of time.
But before you start typing away, it’s better to take a step back and make sure you understand the task ahead of you. This is especially true during technical interviews: asking questions before you start to solve the problem demonstrates that you are taking a thoughtful approach.
In How To Solve It, Polya describes this phase of the problem-solving process as follows:
First of all, the verbal statement of the problem must be understood. The teacher can check this, up to a certain extent; he asks the student to repeat the statement, and the student should be able to state the problem fluently. The student should also be able to point out the principal parts of the problem, the unknown, the data, the condition. (pp 6–7)
This gives us a few helpful benchmarks when approaching a problem. Here are some questions you should ask yourself before trying to embark on a solution:
1. Can I restate the problem in my own words?
2. What are the inputs that go into the problem?
3. What are the outputs that should come from the solution to the problem?
4. Can the outputs be determined from the inputs? In other words, do I have enough information to solve the problem? (You may not be able to answer this question until you set about solving the problem. That’s okay; it’s still worth considering the question at this early stage.)
5. How should I label the important pieces of data that are a part of the problem?
Asking all these questions of yourself may seem like a lot. But like any other skill, problem-solving requires practice to develop. Over time, if you ask these questions of yourself repeatedly, you’ll begin to internalize them. Eventually, they’ll just become a natural checklist that you go through almost without thinking about it.
Imagine you’re in an interview and are asked to do the following:
Write a function which takes two numbers and returns their sum.
Even for a problem that’s as straightforward as this, it’s worth asking yourself the questions listed above. Let’s take a look at each one in turn:
1. Can I restate the problem in my own words? Maybe something like “Write a function that adds two numbers,” or “Implement addition.”
2. What are the inputs that go into the problem? Two numbers. This might seem like an open and shut case, but even here it’s worth thinking about the structure of the inputs. For instance, are the numbers integers, or are floats allowed too? Should the inputs even be numbers at all, or strings representing numbers? If the latter, the problem suddenly becomes much harder, because you can represent arbitrarily large numbers with strings. For example,
100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
It’s also worth asking whether the input always consists of just _two_ numbers, or whether it’s possible there could be more inputs (or fewer).
3. What are the outputs that should come from the solution to the problem? One number, representing the sum of the inputs. Here too, it’s worth thinking about type: should the output be a number, a string, or something else?
4. Can the outputs be determined from the inputs? It seems that way, though the answer depends a bit on the answer to 2. For instance, if it’s possible that there could be fewer than two inputs, it’s not necessarily clear what the output should be if the user doesn’t pass in any arguments at all. (Note how we’re already finding examples of edge cases, before we’ve started writing any code!)
5. How should I label the important pieces of data that are a part of the problem? This is a matter of personal preference, but it seems like there are four things that could use labels: the function, the two inputs, and the output. Names like ‘add,’ ‘num1,’ ‘num2,’ and ‘sum’ seem suitably descriptive.
Once you feel like you have a good understanding of the problem, it’s time to move on to the next problem solving strategy. We’ll discuss this one next time.