<CodeLearn/>
アルゴリズム レッスン1

アルゴリズムとは

計算量、Big O記法、効率的な問題解決の基礎を学ぼう

アルゴリズムとは何か?

アルゴリズムとは、ある問題を解決するための明確な手順の集まりです。 料理のレシピのように、入力(材料)を受け取り、決められたステップに従って処理し、 出力(完成した料理)を返します。

同じ問題に対して複数のアルゴリズムが存在することが多く、 それぞれ処理速度やメモリ使用量が異なります。適切なアルゴリズムを選ぶことで、 プログラムの性能を大幅に改善できます。

// 例:配列の中から最大値を見つけるアルゴリズム
function findMax(arr) {
  let max = arr[0];
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] > max) {
      max = arr[i];
    }
  }
  return max;
}

console.log(findMax([3, 7, 2, 9, 1])); // 9

計算量とBig O記法

計算量(Complexity)とは、 アルゴリズムの効率を測る指標です。主に時間計算量(処理にかかる時間)と空間計算量(メモリ使用量)の2種類があります。

Big O記法は、入力サイズ n が大きくなったとき、 アルゴリズムの処理時間がどのように増加するかを表す記法です。

O(1) - 定数時間

入力サイズに関係なく一定時間。配列のインデックスアクセスなど。

O(log n) - 対数時間

入力が倍になっても1ステップしか増えない。二分探索など。

O(n) - 線形時間

入力サイズに比例して時間が増加。配列の全探索など。

O(n log n) - 準線形時間

効率的なソートアルゴリズム(マージソート、クイックソート)。

O(n^2) - 二乗時間

入力が倍になると4倍の時間。二重ループ、バブルソートなど。

時間計算量の具体例

実際のコードでどのように計算量が変わるか見てみましょう。

// O(1) - 定数時間:配列のインデックスアクセス
function getFirst(arr) {
  return arr[0]; // 配列の大きさに関係なく一瞬
}

// O(n) - 線形時間:全要素を1回ずつ走査
function sum(arr) {
  let total = 0;
  for (let i = 0; i < arr.length; i++) {
    total += arr[i]; // n回のループ
  }
  return total;
}

// O(n^2) - 二乗時間:二重ループ
function hasDuplicate(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) return true;
    }
  }
  return false;
}

空間計算量

空間計算量は、アルゴリズムが使用するメモリの量を表します。 入力データ自体のサイズに加えて、処理中に追加で必要になるメモリ(補助空間)を考えます。

// O(1) 空間 - 追加メモリなし(変数のみ)
function findMax(arr) {
  let max = arr[0]; // 変数1つだけ
  for (const val of arr) {
    if (val > max) max = val;
  }
  return max;
}

// O(n) 空間 - 入力と同サイズの配列を新規作成
function reversed(arr) {
  const result = []; // n個分のメモリ
  for (let i = arr.length - 1; i >= 0; i--) {
    result.push(arr[i]);
  }
  return result;
}

アルゴリズムの選び方

アルゴリズムを選ぶ際は、以下のポイントを考慮します。

  • 入力サイズ:データが小さければ単純なアルゴリズムでも十分
  • 時間 vs 空間のトレードオフ:メモリを使って速度を上げるか、メモリを節約するか
  • データの特性:ほぼソート済みか、ランダムか、重複があるか
  • 実装の複雑さ:シンプルなコードの方が保守しやすい
// 例:重複チェック - O(n^2) vs O(n) のトレードオフ

// 方法1: O(n^2) 時間 / O(1) 空間 - メモリ節約
function hasDupSlow(arr) {
  for (let i = 0; i < arr.length; i++)
    for (let j = i + 1; j < arr.length; j++)
      if (arr[i] === arr[j]) return true;
  return false;
}

// 方法2: O(n) 時間 / O(n) 空間 - 速度重視
function hasDupFast(arr) {
  const seen = new Set();
  for (const val of arr) {
    if (seen.has(val)) return true;
    seen.add(val);
  }
  return false;
}

まとめ

  • アルゴリズムは問題を解くための明確な手順の集まり
  • Big O記法で計算量(効率)を表現する
  • 時間計算量と空間計算量の2つの観点がある
  • O(1) < O(log n) < O(n) < O(n log n) < O(n^2) の順に遅くなる
  • データの特性や要件に応じて最適なアルゴリズムを選択する