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

NP過大評価

2026年8月14日原文(gruhn.me)

概要

NP-hard問題 は理論的には難解だが、実務では必ずしも扱えないわけではない 多くの人が「NP-hard=絶対に解けない」と誤解しがち 現実の入力では 高速解決 できるケースが大半 アルゴリズムの進化は ハードウェア進化 を凌駕 最悪ケースにも現実的な 対処法 が存在

NP-hard問題の誤解

  • NP-hard問題 は理論上「計算量的に困難」だが、現実では多くの場合 実用的な解決 が可能
  • 大学の講義やネット上で「NP-hardだから無理」といった 悲観的な意見 が広まっている現状
  • 理論 は正しいが、実務では「ほとんどの入力で高速に解ける」ことが多い
  • 最悪ケース が発生することは理論上否定できないが、99.9%以上の現実的な入力では問題なし
  • Benjamin Brewster の言葉「理論上、理論と実践に違いはない。しかし実践上は違う」が象徴的

NP-hard問題の実例と現実

  • 代表的なNP-hard問題
    • Dependency resolution (パッケージマネージャの依存解決)
    • Type checking (一部の型システム)
    • Scheduling (スケジューリング)
    • Traveling Salesman (巡回セールスマン問題)
    • Boolean Satisfiability (SAT) (ブール充足可能性問題)
  • (1)と(2)は最悪ケースが現実にはほぼ発生しない
    • パッケージインストール型検査 で壊滅的な遅延は実務上ほぼ未経験
  • (3)と(4)は最適化問題
    • ヒューリスティクス で十分に対応可能
    • 必ずしも「最適性」を犠牲にしなくても良い場合も多い
    • 最適解を現実的な時間で見つけるアルゴリズム も存在
  • SAT問題 はNP-hardの代表格だが、現代では大規模でも日常的に解決
    • 例: Amazon は1日10億件のSMT問題(SATより難しい)を解決
    • SATソルバー の進化で「SATは簡単な部類」とまで言われる

アルゴリズム進化と実務的対処

  • アルゴリズムの進化は ハードウェア進化 を大きく上回る
    • 例:1991年から2015年で 4500億倍 の高速化(論文による)
  • 最悪ケースに遭遇した場合の現実的対応
    • タイムアウト設定
    • エラーメッセージ表示
    • リトライやスキップ などの運用的回避策
  • HTTPリクエスト が失敗するのと同様、NP-hard問題も「失敗時の扱い」を設計すれば良い

まとめと現場での心得

  • NP-hard問題 は「絶対に解けない」ではなく「最悪ケースで困難」
  • 現実の多くのケースで 実用的な速度 で解決可能
  • アルゴリズム設計現実的な運用 で十分に対応できる
  • 悲観的な神話に惑わされず、 現実的な視点 で問題解決を目指す姿勢が重要

Hackerたちの意見

確かにそうだね!NP困難な問題が難しいのは、特定の問題構成に関連する組み合わせ爆発がほとんどだから。おおよそのヒューリスティックや分岐限定ソルバーを使えば、指数関数的に膨れ上がるインスタンスを作れるんだ。でも、実際の問題では、そんな爆発的な構成にはならないことが多いよね。特定のNP困難な問題のクラスに対して、何らかの定量化ができるかもしれない。面白いのは、多くのアルゴリズム(特に暗号学の分野)は、そういった組み合わせのエッジケースを作るように設計されていること。普通の問題を扱うSATソルバーは素晴らしい仕事をするけど、SHA256を扱うSATソルバーはあまり良くない。実際、暗号システムを開発する科学は、ヒューリスティック近似に対して抵抗力のある指数関数的な爆発を見つける科学だと言えるね。

あなたが探しているフレーズは「位相転移」だと思うよ。ここで議論されてるよ: https://cstheory.stackexchange.com/questions/33550

この脳を広げる話、TFAの精神に共感するな。「旅行セールスマン問題は、大きなグラフのクラスではO(N)だって知ってた?」もう一つの洞察として、賢いO(logn)の解法が、ほとんど分岐なしのO(N)の前処理によって完全に打ち消されることがよくあるんだ。その後、コンピュータが得意とする問題、例えば連続メモリアクセスやベクトル演算が続くとね。

第二のクラスの例って、何かある?

なんで「アルゴリズム」が、1970年代のコンピュータを模倣するために頭の中でやることになってるんだろう?実際のハードウェアで起こることじゃないのに。

書いてある内容が、一番の解決策である「難しい問題を許可しない」にあまり触れてない気がする。依存関係マネージャーは、実際にNP困難な空間全体を排除するような状況をブロックすることが多いし、型システムも同様に明示的に区切られてる。トリックは「それでもやる」じゃなくて、一般的な問題が「不可能」だと認めることだよ。だから、最善を尽くすか、不可能なものを排除するかのどちらかだね。

別の見方をすると、実際にはNは大きな定数で制約されることが多くて、時間計算量は実質的にO(1)になるんだ。特に依存関係の解決に関しては、可能な依存関係のセットは、エコシステム全体でおそらく100から10000の範囲にあると思う。エコシステム内のパッケージ数が増え続けてもね。

難しい問題を許可しないって、問題をPにするってことだよね?

依存関係マネージャーは、実質的にNP困難な空間全体を排除する巨大なカテゴリをブロックする傾向がある このことについて詳しく教えてもらえる?多くの人がこれを回避しようとするけど、例えばCargoの https://doc.rust-lang.org/cargo/reference/resolver.html#semv... みたいに、でもそれはPには入っていない。Nixは依存関係の解決を*2nixツールにオフロードする。Goの最小バージョン選択は単なるツリーウォークだけど、かなりの表現力を失っている。

バリアント: > 難しいものに遭遇しない 例えば、線形計画法の単体法では、難しいインスタンスを排除することは何もしない。問題が来たらそのまま解くだけで、解くように頼まれたものは決して難しいものにはならない。(もちろん、一般化しているけど。)

「(1)と(2)について、最悪のケースは起こらないんだ。パッケージのインストールや型チェックは確かに遅くなることもあるけど、少なくとも僕のキャリアでは、銀河規模の膨張を見たことはない。NP困難な問題は正確に解くのが難しいけど、効率的にかなり良い近似解を得ることは通常可能だよ。ただ、いくつかの探索問題は、近似的にも非常に難しいんだ。古いDebianを大きなアップグレードを経てaptitudeで使ってきたなら、外部の探索空間で迷子になるのを何度も見たことがあるはず。時にはaptitudeがパッケージをダウングレードしたり、アンインストールしたり、推奨パッケージをインストールしない必要があって、正しい解にたどり着くんだ。ダウングレードできるパッケージはたくさんあって、それぞれが新しい可能性を持つ新たな混乱を生むんだ。これは他のパッケージマネージャーでは経験しないことで、もし手助けしないと、その探索戦略は本当に解決不可能になるよ。小さなパッケージのセットを手動で特定しないといけないからね。」

あなたがaptについて説明した内容は、SATにほとんどそのまま当てはまる、非常に効率的に近似される問題に聞こえる。最近のaptバージョンでの--solver 3.0フラグがまさにそういう風に機能するんだ。[0] https://blog.jak-linux.org/2024/05/14/solver3/

Hacker Newsで議論の続きを見る