← 戻る

計算理論とアルゴリズム

"プログラミングを学ぶとは、コンピュータの可能性と限界を理解することである" — Knuth風

1チューリング完全性

なぜ JavaScript と Python と Lisp が「同じこと」を計算できるのか? 答えは「チューリング完全」だから。

チューリングマシンとは

1936年、アラン・チューリングが論文『計算可能数について』で提示した抽象機械。無限のテープ読み書きヘッド状態だけで、計算可能なすべての関数を表現できる。

📼 仮想チューリングマシン: 0と1を反転する

紫=ヘッド位置。1なら0に書き換え、0なら1に書き換え、右に移動

「チューリング完全」の意味

ある言語/システムが チューリングマシンと同じ計算能力を持つとき、それは「チューリング完全」と呼ばれる。すなわち、原理的に 計算可能なものは何でも計算できる

チューリング完全な言語

JavaScript, Python, Java, C, Ruby, Rust, Go, Lisp, Haskell... ほぼすべての汎用言語。

驚くべきことに Excel関数(LET/LAMBDA以降)Magic: The GatheringPowerPointもチューリング完全。

チューリング完全でないもの

正規表現(基本機能のみ)、SQL(基本機能のみ)、HTML(マークアップのみ)、CSS(一部議論あり)。これらは表現力に制限があるかわりに、解析しやすく安全。

つまり「言語選び」は 何ができるかではなく どう書きやすいか・どう速いか・誰が読めるかの選択である。

"すべてのプログラミング言語は、Lambda計算かチューリングマシンの上に座っている。違いは飾りである。"— Lisp系プログラマ風

2停止性問題(Halting Problem)

「あるプログラムが永遠にループするか、いつか止まるか」を判定するプログラムは 原理的に作れない。これは数学的に証明されている。

なぜ作れないのか(背理法)

もし「Halt(P, x)」という、プログラム P と入力 x が停止するなら true を返す関数があったとする。次の意地悪なプログラムを考える:

function Naughty(p) {
  if (Halt(p, p)) {
    while(true) {} // 永遠ループ
  } else {
    return; // 即座に停止
  }
}

Naughty(Naughty); // ← これは停止する?しない?

Naughty(Naughty) が 停止すると仮定 → Halt が true → 永遠ループ → 停止しない。矛盾。
Naughty(Naughty) が 停止しないと仮定 → Halt が false → 即座に停止。矛盾。

よって Halt は存在しない。Q.E.D.(証明終了)

実用上の意味

"プログラマが直面する多くの問題は、停止性問題に還元される。完璧を諦めたとき、実用が始まる。"— Dijkstra風

3Big-O 記法 — 計算量の表し方

同じ問題でも、アルゴリズムによって速度が 100万倍違う。Big-O はその違いを表す共通語。

定義

f(n) = O(g(n)) ⟺ ∃c, n₀: ∀n ≥ n₀, |f(n)| ≤ c·|g(n)|

意訳: 入力サイズ n が大きくなったとき、最終的に g(n) の定数倍以下に収まる。定数とサブ項は無視する。

主要な複雑度(速い順)

記法名前n=1000で評価
O(1)定数時間配列の要素アクセス1回最速
O(log n)対数時間二分探索約10回優秀
O(√n)平方根素数判定の試し割り約32回良い
O(n)線形時間配列の全走査1,000回良い
O(n log n)準線形マージソート、クイックソート約10,000回実用上限
O(n²)二乗時間バブルソート、二重ループ1,000,000回小データOK
O(n³)三乗時間素朴な行列積10⁹回注意
O(2ⁿ)指数時間素朴なフィボナッチ、巡回セールスマン宇宙が終わる前に終わらない絶望
O(n!)階乗時間すべての順列を試すもっと絶望
入力サイズ n → 時間 → O(log n) O(n) O(n log n) O(n²) O(2ⁿ)

実例: 配列に値が含まれるか調べる

O(n) - 線形探索
for (let x of arr) {
  if (x === target) return true;
}
return false;

n=10⁹なら数秒〜数分

O(log n) - 二分探索(要ソート)
let lo=0, hi=arr.length-1;
while(lo<=hi) {
  const mid=(lo+hi)>>1;
  if(arr[mid]===target) return true;
  if(arr[mid]

n=10⁹でも30回程度

"早すぎる最適化は諸悪の根源だ。だが、誤ったアルゴリズムを選ぶことは別の話である。"— Donald Knuth

4複雑性クラス — P vs NP問題

100万ドルの懸賞金がかかった、計算機科学最大の未解決問題。

主要クラス

P (Polynomial)

多項式時間で解ける問題。ソート、最短経路、行列積など。「効率的に解ける」と同義。

NP (Nondeterministic-P)

多項式時間で検証できる問題。解を1つ示されたら正しさをすぐ確かめられる。

NP困難 (NP-hard)

NP より難しいか同等以上の問題。多くは指数時間しか知られていない

NP完全 (NP-complete)

NPの中で最も難しい問題群。1つでも多項式時間で解ければ P=NP となり全問題解決

NP完全問題の例

これらは現実世界(物流、暗号、設計、スケジュール)に大量に存在。完全解は無理なので近似アルゴリズム・ヒューリスティック・SAT solverが活躍する。

"P=NP が証明されれば、暗号は崩壊し、人類は新時代に入る。多くの研究者は P≠NP だと信じているが、誰も証明できていない。"— Clay Mathematics Institute

5主要アルゴリズム30選

名前を聞いたら反応できる必修アルゴリズム。Knuth『TAOCP』の入口。

ソート

探索

グラフ

動的計画法 (DP)

文字列

暗号・数学

6言語別パフォーマンス感覚

同じことをやらせたとき、言語によって 100倍以上差が出る。Carmackの世界観。

素朴ベンチマーク(C基準)

言語相対速度メモリ効率備考
Assembly1.0x(最速)最良人間より速く書けない
C1.0x最良事実上の基準
C++1.0x最良抽象化のオーバーヘッドはほぼない
Rust1.0-1.1x最良安全性とのトレードオフほぼゼロ
Zig1.0-1.1x最良Cと同等
Fortran1.0x最良数値計算ではC超えも
Go1.5-3xGC込みでこの速度は驚異
Java (JIT)1.5-3xJIT最適化が効くとCに迫る
C# (.NET)1.5-3xJavaとほぼTie
Swift1.2-2xCに近い
Kotlin (JVM)1.5-3xJava同等
JavaScript (V8)2-5xJIT次第で速い
Julia1-2x科学計算ではCを超えることも
Lua (LuaJIT)1-3xJIT版は驚異的
OCaml1.5-3x関数型ながら高速
Haskell2-5x遅延評価コスト
Erlang/Elixir10-30x並行性で勝負
Python (CPython)30-100x遅さが弱点だが書きやすさで補う
Ruby30-100xPythonと同等
PHP5-30xPHP 8で大幅高速化
R30-100xベクトル化で実用速度
Perl10-50x
VBA50-200xExcel依存で遅い
Scratch1000x+速度より教育目的

※あくまで素朴な目安。実際は実装・最適化・問題内容で大きく変動。Pythonでも NumPy/PyTorch 経由なら C 速度。重要なのは 「ホットスポット」を見つけて適切な言語/ライブラリを使うこと。

選び方の指針

速度が最重要

C / C++ / Rust / Zig / Fortran
ゲームエンジン、HFT、OS、組み込み

速度と書きやすさのバランス

Go / Java / Swift / Kotlin / C#
Webサーバー、モバイル、エンタープライズ

書きやすさ・生産性最優先

Python / Ruby / JavaScript
スクリプト、Webサービス、AI

並行性が最重要

Erlang / Elixir / Go
チャット、通信、ライブ配信

7設計パターンの基本

GoF (Gang of Four) パターン入門。再発見し続けてきた「形」の名前。

生成パターン

構造パターン

振る舞いパターン

関数型パターン

"パターンに名前を与えることは、議論を可能にすることである。"— Christopher Alexander(建築家、GoFが影響を受けた)