世界を動かす技術を、日本語で。

「動的計画法」は「コンピュータプログラミング」を指しているわけではない

概要

Dynamic Programming (動的計画法)の「programming」は 「計画立案」 の意味 1950年代 にRichard Bellmanが命名した 背景 依存関係 に基づく 最適な手順の構築 Top-downBottom-up 両方のアプローチが存在 「dynamic」の意味より「programming」の意味に 注目 する重要性

Dynamic Programmingの「programming」とは

  • Dynamic Programming の「programming」は 「ソフトウェアを書く」 意味ではなく 「計画を立てる」 意味

  • Oxford English Dictionary によると、「programming」は 管理や統制のための計画立案

  • TV programming (番組編成)のような スケジューリング に近い意味合い

  • 1950年代、土木技師が「新しいビルをプログラムする」と言えば、 建設工程の計画立案 を指す

  • 例: 基礎工事→構造→内装→検査 という 依存順序のある作業計画

    • 各作業は 前段階の完了 が必要
    • 一度済ませた作業を 繰り返さない効率化 が重要

Dynamic Programmingの本質

  • Dynamic Programming は「問題解決のための 最適な手順計画」の構築
  • 例: Fibonacci数列 を求める場合、fib(2)からfib(10)まで 依存順に計算
    • 各fib(n)は fib(n-1), fib(n-2) の計算結果に依存
    • 同じ値を何度も計算しない ための手順設計
  • Top-down(再帰的分割)Bottom-up(逐次積み上げ) いずれのアプローチも Dynamic Programming に該当
  • 依存関係 を整理し、 無駄を省く計画 の立案が核

「dynamic」と「programming」の語源・背景

  • Richard Bellman が1950年代に命名
  • 米国防長官Wilsonは「research」や「mathematical」という言葉を 嫌悪
  • Bellmanは RAND Corporation での研究活動を 目立たせないため に「dynamic programming」という名前を採用
  • 「dynamic」 は「多段階」「時間変化」「否定的な意味を持たない」形容詞として選択
  • 「programming」計画意思決定 の意味
  • 「dynamic programming」計画的・段階的な最適化手法 として広まる

誤解されやすい「dynamic」の意味

  • 多くの人が「dynamic」の意味ばかり気にするが、 本質は「programming」
  • 「dynamic programming」 は「動的なプログラミング」ではなく 「動的な計画立案」
  • 「dynamic typing」 など、他の文脈での「dynamic」とは異なる意味

まとめ

  • Dynamic Programming の「programming」は 「計画」
  • 問題の 依存関係を整理し、無駄なく解決する計画 の策定
  • Top-down/Bottom-up どちらも 本質は同じ
  • Richard Bellman の命名背景を知ることで 誤解が解ける

Hackerたちの意見

それに、「動的プログラミング」という用語は、LeetCodeの問題と強化学習の文脈で少し意味が違うんだよね。前者は比較的小さな決定論的システムを最適化するためのもので、後者は確率過程を最適化するためのもの。ちょっと混乱するよね。

ベルマンが考えた動的プログラミング(ある決まった手順のこと)は、今の普通のプログラミングよりも厳密にはあまりダイナミックじゃないから、混乱するのかな?「メモ化+再帰」を「動的プログラミング」として昇格させるのは、ベルマンが予想していなかった発展のような気がする。 https://cs.stackexchange.com/questions/99513/dynamic-program... それに、奇妙に一般的な何かに対して奇妙に特化した別の中世紀の用語「オペレーションズリサーチ」があるね。ベルマンは「リアクティブ最適化」と呼ぶべきだったのかも。そうすれば、完全に適切な「再帰メモ化」や「メモ化再帰」ができたのに。

動的プログラミングの「プログラミング」は、線形プログラミングや整数プログラミング、制約プログラミングの「プログラミング」と同じように考えた方がいいよ。実際、素人の言葉で言っても、テレビ番組のプログラミングのように考える方が、コンピュータプログラミングに関連していると考えるよりも正確だよね(TFAでも言われてるけど)。

ステップの順序はダイナミックプログラミングの結果なんだ。すべてのステップの順序がダイナミックプログラムというわけではないよ。そして(私が主張するには)核心的な結果はかなり数学的で、メモ化や再帰は登場しないけど、偏微分方程式は出てくるよ。ハミルトン・ヤコビ・ベルマン方程式をチェックしてみて。

「メモ化+再帰」を「ダイナミックプログラミング」に昇格させるのは、ベルマンが予想していなかった展開のようですね。 まあ、実際にはそんな展開は起こってないけど。ダイナミックプログラミングは、常にメモ化を使った再帰として実装できます。でも、再帰的でメモ化されたアルゴリズムが必ずしもダイナミックプログラミングの例になるわけではないです。ダイナミックプログラミングのポイントは、再帰呼び出しがキャッシュにヒットするように再帰を設定することです。キャッシュがあって、運が良ければヒットするってわけじゃないんです。

スリランカから来たよ。90年代にIOI(国際情報オリンピック)に出場してたんだ。スリランカでは競技に勝ちまくったけど、初めて国際大会に出たときはメダルを取れなかった。再帰を使っていくつかの問題を解いたけど、厳しい時間制限内に終わらなかったんだ。他の国の参加者に「問題1と5を解くには動的プログラミングが必要だったよ」と言われた。その後、1年間ずっと動的プログラミングが何かを探し続けた。こっちの人たちはその用語に詳しくなかったんだ。実際、「動的メモリ割り当てのこと?」ってよく聞かれた。しばらくして、動的プログラミングについて書かれた本を見つけて、「ああ、これは再帰+結果を保存することなんだ」とか「特定の状況では反復的にやることもできるんだ」と思った。要するに、この新しい知識を持って、2001年のIOIで金メダルを取れたんだ。楽しい思い出だよ。

私も同じ経験をしたよ:私のメンターたちも「動的プログラミング」が何か知らなかったし、我が国の競技(IOIにインスパイアされた問題を作った)では5問中2問が動的プログラミングを必要としたんだ。当然、初めては失敗した(2004年)。その後、動的プログラミングが何かを学んで、次の年(2005年)には成功したよ。

1997年のケープタウンでのIOIのホスティングチームに参加してたんだけど、君が言ってたような参加者との会話をしたり、ダイナミックプログラミングについても学んだよ。君の「再帰 + 結果を保存する、時には反復的にできる」という説明が一番の要約だね。いい例としては、反復的なフィボナッチアルゴリズムがあるよ。あの時の面白いエピソードとして、参加者のマシンにシリアルポート経由で接続するスコアリングプログラムには特別な注意が必要だったんだ。前の年に、一人の参加者がシリアルポートのインターフェースをハッキングして結果を操作してたからね。 :-)

プログラミングコンテストで「軍拡競争」みたいなことがあったのかな? 知識が広がって、より難しい問題を選ぶ必要が出てきてるよね。最近ではダイナミックプログラミングはICPCタイプの問題の基本的な要素になってるし。

あなたの素晴らしいストーリー、めっちゃ好きです!私のは普通の話ですけど。卒業してからプログラミングを始めたんですが、ダイナミックプログラミングはスタンフォードのアルゴリズムのコースを見てやっと理解できました(MOOCに感謝!)。でも正直言って、実際に役立つことはあまりしてないです。普通の物事に新しい視点を与えてくれたくらいかな。例えば、太極図には大きな知恵があるかもしれないって気づきました(ここに画像があります: https://commons.wikimedia.org/wiki/File:Yin_yang.svg)。今の問題を分けて、下のレベルで解決できるんです(陰陽の点みたいに)。もちろん、その下のレベルにもさらに分割や計算があって、最後には何も残らなくなるまで続きます。一方で、基礎から始めて上に構築していく(ボトムアップ)こともできて、高レベルの計算に向かって進むことができます。そこでまた分割が待っていて、最終的には一つの大きな統一された答えが出てくるんです。実際の使い道としてはあまり役に立たないですよね?^^

IOI 2001の記憶を整理できるかな?初日の結果が遅れたんですが、参加者が問題の一つで検索をコンパイル時のC++テンプレートメタプログラムにエンコードしたからだと思います。二日目には、解答にもコンパイル時の制限があったような気がします。もっと詳しいこと覚えてますか?

ずっと不思議に思ってたことがあるんだけど、ダイナミックプログラミングが普通のプログラミングより「ダイナミック」ってどういうことなんだろう? 再帰は「静的」だけど、結果を保存すると「ダイナミック」になるのはなんで?

あ、それ私が行った年だ。懐かしい思い出。全然準備が足りなかったな。モバイルフォンのセルサマレーション問題で、自分がどれだけ知らないかを痛感したし、その後どれだけ学ばなきゃいけないかもわかった。それがこの分野への愛を深めたんだ。経験が本当に楽しかった。

アメリカ生まれの英語ネイティブとして、あのフレーズにはいつも苦労してた。結局、実際の問題セットに価値を与えないから、頭の中では誤称だと思うようにしてた。

初めてのプログラミングコンペ(2002年の全国ICPC予選)で、チームメイト(あまり知らない人たちで、急いでチームを作ったばかり)にコンテストの数分前に「ダイナミックプログラミングって何?」って聞いたんだ。そしたら一人が「それは再帰関数の出力をキャッシュするだけだよ」って言ってた(厳密にはメモ化だけど、気にしないで)。締切の数分前にDPを使って問題を解決できて、結果的に3位になってICPCに進めた。楽しい時間だったな。

何かの組み合わせを考えてみて、[動的]に否定的な意味を持たせることができるかもしれない。無理だよ。 > *このHaskellファンの提案は「動的型付け」 :P 何事も不可能じゃない!

確かに、これは婉曲表現として使えるし、場合によっては悪口にもなるね。「ダイナミックな真実性」とか「ダイナミックな道徳感」とか。

ダイナミックスコープもあるよ。これはレキシカルスコープ、つまり静的スコープとは対照的だね。ダイナミックタイピングの擁護者はいるけど、ダイナミックスコープは今では間違いだと広く見なされてる。少なくともデフォルトでダイナミックスコープはそうだけど、特別な使い方もあるし、HaskellではReaderモナドがダイナミックスコープとほぼ同型だから、誰も文句言わないよ。

コンピュータ関連で最悪の名前のものかも?「プログラミング」がプログラミングを指してないだけじゃなくて、「動的」も意味がなくて、ただかっこよく聞こえさせるためにあるだけ。私は「キャッシュされた再帰」の方が好きだな。

うーん、キャッシュ再帰はちょっと一般的すぎるかな。キャッシュと再帰を使ってできることはほとんど何でもあるよ。数学的最適化の授業では、ダイナミックプログラミングを「グラフの最長経路を見つけるのと同型の問題」と定義してたんだ。ここでの「最大化」や「加算」の「オーバーロード」について説明すると、行列の掛け算は通常は掛け算(×)と加算(+)を使うけど、ミン・プラス代数では、加算を最小値に、掛け算を加算にオーバーロードすると、行列の掛け算がグラフの隣接行列の2つの経路を跳ぶことと同じになるんだ。(ちょっと混乱させてたらごめんね。)具体的には、隣接行列Aを使ってA* := 1 + A + AA + AAA + ...を計算することは、グラフのエッジホッピングの推移閉包を求めることと同じで、すべての頂点のペア間の最短経路になるんだ。もし加算を最大値に、掛け算を加算にオーバーロードすると、最長経路を見つけられるよ。それにはグラフにサイクルがないことが条件だけどね。さて、ダイナミックプログラミングに戻ると、ダイナミックプログラミングの共有最適部分構造特性は、最長経路を見つけるのとまったく同じなんだ。この構造は制約が厳しいように聞こえるかもしれないけど、ダイナミックプログラミングのほとんどの例に適用できることがわかるよ。P.S. ちょっと記憶が曖昧だったから、ChatGPTに助けを求めたら、どうやら完全に間違ってはいなかったみたいだね。https://chatgpt.com/share/687de24c-c674-8009-9984-0fda56d1c1... P.P.S. 上で言ったこととは裏腹に、「キャッシュ再帰」はダイナミックプログラミングよりもまだ良い名前だと思うよ。

実際、メモ化された再帰に過度に焦点を当てると、ダイナミックプログラミングの核心的な概念を損なうと思うんだ。投稿にも書いてあるように、プログラミングという用語は計画のための表形式の方法を指していて、つまり具体的には問題を解決するためのボトムアップ計算を指してるんだ。ここでの強調点は、望ましい結果に到達するための計画を表にまとめる順序があらかじめ決まっていることなんだ。それが広い問題の解決策になる。メモ化された再帰関数は、どの順序で表を作成すべきかという問題を特に回避するデジタルコンピュータ上の実装方法なんだ。私は、再帰的メモ化はダイナミックプログラミング問題(ダイナミックプログラミングの解決策を許容する問題の宇宙として定義される)に対する可能な解法技術だと主張するけど、厳密に言えば、それ自体はダイナミックプログラミングではないよ。

「キャッシュ再帰」は広すぎると思うな。私は「最小メモリメモ化」にすることが多いよ。1) 再帰的な解法は遅すぎることが多いけど、メモリはあまり使わない。2) メモ化された解法は、1のアルゴリズムをかなり速くするけど、メモリ使用量が膨れ上がる。3) ダイナミックプログラミングの解法は、将来の部分解に必要な前の部分解だけをメモリに保持するんだ。だから「最小メモリメモ化された」解法になる。これは、部分解の賢い順序が必要で、最も早くキャッシュを解放できるようにすることが多いよ。君の「キャッシュ再帰」は私には2番目のように聞こえるし、ダイナミックプログラミングの要点は、いつキャッシュからエントリーを削除するかを見極めることなんだ。

ダイナミックは絶対に無意味じゃないですよ。私は最適制御理論の観点からダイナミックプログラミングに詳しいですが、ダイナミカルシステムに焦点を当てています。ダイナミックプログラミングの中心方程式はハミルトン-ヤコビ-ベルマン方程式で、古典力学のハミルトン-ヤコビ方程式の一般化です。この文脈での「プログラミング」という用語は、線形プログラミングや確率的プログラミングにも存在します。今は混乱するかもしれませんが、これらの用語は現代のプログラミングやコンピュータよりも前から使われていたものです。正直、コメント欄でみんなが再帰+メモ化について話しているのを見てちょっと戸惑っています(効率的に逆帰納法を実装する方法として?)。コンピュータサイエンスはダイナミックプログラミングのアイデアの特定の応用や実装を教えていて、それを全体のものと勘違いしている人が多いのかな?

そうですね。私は気にしないけど、この分野は実際のことについて何も伝えない名前がたくさんありますからね。

わざとわかりにくくしてるんだね。記事によると、説明じゃなくてプロモーションのためなんだって。

プログラミング用語は「線形プログラミング」と同じようなもんだよね… ウィキペディアによると、その起源のストーリーは少し議論されてるみたい。「ラッセルとノーヴィグによれば、上記の話は「厳密には真実ではない。なぜなら、ベルマンがこの用語を使った最初の論文(1952年)は、ウィルソンが1953年に国防長官になった前に発表されたからだ。」また、ハロルド・J・クシュナーはスピーチで「一方で、私が[ベルマン]に同じ質問をしたとき、彼は動的を加えることでダンツィグの線形プログラミングを上回ろうとしていたと答えた。おそらく両方の動機が真実だったのだろう。」と言っている。

リソースは見つからないけど、ベルマンによれば、彼は軍隊が使うような名前にはしたくなかったらしいです。要は、彼の研究を守るためですね。いくつかの選択肢があって、「ダイナミック」は軍隊にとって「クール」な響きだったみたいです。そういう意味では、「プログラミング」の部分はかなり正確だけど、個人的には著者が「コンピュータプログラミング」の意味を理解してない気がします。

線形プログラミングはプログラミングと混同されるけど、線形代数や線形モデルとも混同されることがあるよね。

誰かがこのredditの投稿[1]を見つけて、トップの回答を元にブログを書いたみたいですね。例がそのままコピーされているので、ウェブ検索に接続されたLLMかもしれません。[1] https://www.reddit.com/r/learnprogramming/comments/1ac9zbl/d...

私のお気に入りのダイナミックプログラミングの問題は、与えられた人々のセットとその中の親子関係の部分的なグラフから、二人の人の関連度のパーセントを計算することです(グラフで示されていない関連度はゼロと仮定)。最初はすごく混乱するけど、AがBの子孫でない場合、rel(A,B)=(rel(A,father(B))+rel(A,mother(B)))/2となることに気づきます。これで、グラフの上から下まで関連度の値をできるだけ早く計算できるんです。

共有してくれてありがとう。私はいつもDPは部分的な結果をキャッシュするものだと思ってた。普段のアルゴリズムではあまりやらないことだよね。名前が実行順序の最適化に関連してるとは思ってなかった。

うん、でも私たちがやってることをなんて呼ぶの? 名前で「サブルーチン」を呼び出せるインタープリタ言語があるけど、プログラムの中でデータから「その場で」名前を作ることもできるよね。