動的プログラミングにより、アルゴリズム設計について考えることができます。これはしばしば非常に役立ちます。
メモ化およびボトムアップメソッドは、繰り返しの関係をコードに変換するためのルール/メソッドを提供します。メモ化は比較的単純なアイデアですが、最高のアイデアはしばしばそうです!
動的プログラミングは、アルゴリズムの実行時間を考えるための構造化された方法を提供します。実行時間は、基本的に2つの数値で決まります。解決する必要のあるサブ問題の数と、各サブ問題の解決にかかる時間です。これにより、アルゴリズム設計の問題について考える便利で簡単な方法が提供されます。再帰関係の候補がある場合は、それを見て、実行時間を非常にすばやく把握できます(たとえば、多くの場合、サブ問題の数は非常にすばやくわかります。これは、実行時間;指数関数的に多くのサブ問題を解決しなければならない場合、再発はおそらく良いアプローチではありません)。これは、サブ問題の分解候補の除外にも役立ちます。たとえば、文字列、接頭辞 S [ 1 .. i ]または接尾辞 S [ j 。。n ]または部分文字列 S [ i 。。j ]は合理的かもしれません(部分問題の数は nの多項式です)が、 Sの部分列で部分問題を定義するのは良いアプローチではないでしょう(部分問題の数は nで指数関数的です)。これにより、再発の可能性がある「検索スペース」を整理できます。S[1..n]S[1..i]S[j..n]S[i..j]nSn
Dynamic programming gives you a structured approach to look for candidate recurrence relations. Empirically, this approach is often effective. In particular, there are some heuristics/common patterns you can recognize for common ways to define subproblems, depending on the type of the input. For instance:
If the input is a positive integer n, one candidate way to define a subproblem is by replacing n with a smaller integer n′ (s.t. 0≤n′≤n).
S[1..n]S[1..n] with a prefix S[1..i]; replace S[1..n] with a suffix S[j..n]; replace S[1..n] with a substring S[i..j]. (Here the subproblem is determined by the choice of i,j.)
If the input is a list, do the same as you'd do for a string.
If the input is a tree T, one candidate way to define a subproblem is to replace T with any subtree of T (i.e., pick a node x and replace T with the subtree rooted at x; the subproblem is determined by the choice of x).
(x,y)xy(x,y)(x′,y′) where x′ is a subproblem for x and y′ is a subproblem for y. (You can also consider subproblems of the form (x,y′) or (x′,y).)
And so on. This gives you a very useful heuristic: just by looking at the type signature of the method, you can come up with a list of candidate ways to define subproblems. In other words, just by looking at the problem statement -- looking only at the types of the inputs -- you can come up with a handful of candidate ways to define a subproblem.
This is often very helpful. It doesn't tell you what the recurrence relation is, but when you have a particular choice for how to define the subproblem, often it's not too hard to work out a corresponding recurrence relation. So, it often turns design of a dynamic programming algorithm into a structured experience. You write down on scrap paper a list of candidate ways to define subproblems (using the heuristic above). Then, for each candidate, you try to write down a recurrence relation, and evaluate its running time by counting the number of subproblems and the time spent per subproblem. After trying each candidate, you keep the best one that you were able to find. Providing some structure to the algorithm design process is a major help, as otherwise algorithm design can be intimidating (there's such a huge space of possible approaches, without some structure it can be unclear how to even get started).