Problem Solving Advice
Not to be confused with Troubleshooting
/ Diagnosis.
It turns out that solving problems can be learned, and taught, and it can
apply to all parts of our lives, not just our work.
Divide and Conquer
Usually, if its a problem that hasn't been solved, its because its complex
and its exceeded our complexity barrier, at least for the moment. To reduce
complexity "divide and conquer". But how should we divide?
-
Top down: Professors at MIT have called this "Wishful Thinking" because
you can say to yourself "Well, this would be easy if I just had a function
that could do this part of it..." and then you imagine that you DO have that
function and wonder what would be inside it. This has the side effect of
organizing your code in a very nice hierarchy. This is taught very intensively
in "Structure and Interpretation of Computer Programs"
^
"Before implementing a component you write some of the code that actually
uses it. This way you discover what functions with what parameters you really
need, which leads to a very good interface. You will also have some good
test code for your component."
""Wishful Thinking" is a good way to think about recursion: When seeing a
recursive call, you should not think about the code being executed on each
sub call. Instead, just imagine that the sub call does its job correctly.
Thus, you prove the correctness of the function by assuming that it is correct
(on simpler input). This is some positive kind of self-fulfilling prophecy.
Of course you still have to check for trivial cases and need to assure that
the recursive call handles a simpler case of your problem. But those are
minor issues. The hardest part is to understand the recursive calls. Without
the technique of wishful thinking, you can't handle problems like "Towers
of Hanoi", let alone more complicated ones. "
-
Bottom up: The "building block" approach. We know what abilities we
have, and we can start using and combining them to build towards the solution
we need.
-
Middle out: AKA "Head On!" where we take on the most difficult part
of the problem, or the part we least understand first and then the rest will
fall into place. This seems counter intuitive, but it is actually the safest
method because it keeps you from wasting time on simple things. See
Iterative Design
-
Vertical slice: In this method, you pick just one feature out of the
many which must be included in your final design, and you implement that
one feature completely. Usually, you start with an important feature, and
implement its UI, its core, its back-end, but just one of many features.
Its the bare minimum to demo it. It might be "minimum viable product". No
frills, no fancy UI, just something that gets ONE useful thing to barely
work. Then you pick the next feature and do the same with that. And then
you try to integrate all those vertical slices together. Sometimes that works
out nicely. Other times, the features don't fit together well since they
have been implemented separately.
Order of Implementation
How do we choose between these methods of dividing up complex problems?
-
If they all seem about the same in difficulty, top-down organizes your code
best so choose that.
-
if we know how to do the very foundation and think we can just "build up"
from there, then use bottom-up.
-
If there's one part that we're thinking about a lot and are interested in
it, go with that. (do what motivates you)
-
The most experienced developers will start middle-out, taking on the "hardest
part, the core guts of the problem" head on, and make that core issue the
new complete problem to solve, and then recursively decide how to break that
up. Iterative Design
-
If there are parts that you think you can easily get help on, then we don't
need to do those. Pick other parts that you alone need to be the one that
solves it. Don't fall into the trap of doing the easy stuff first;
always do the hard stuff first so you can fail
quickly and often and then succeed in the over all task faster.