株式会社ジオデータ開発 ロゴ
株式会社ジオデータ開発GEO DATA DEVELOPMENT Inc.
ハッシュ表ハッシュ関数シノニム衝突剰余演算チェイン法線形探索法負荷率

ALGORITHMS / DATA STRUCTURES / HASH COLLISIONS

ハッシュ表の衝突(シノニム)とは? mod演算・チェイン法・線形探索法を図解で理解する

公開日:2026-10-13

ハッシュ表は、キーから格納先をすばやく求めるためのデータ構造です。辞書のようなデータ、会員番号からの検索、キャッシュの索引など、身近な用途でもよく使われます。この記事では、まず「何に使うのか」をイメージできるようにしたうえで、mod演算による格納先の決め方、シノニム(衝突)、その対策まで順番に整理します。

この記事の学習ルート

ハッシュ表の用途 → ハッシュ関数 → mod演算 → 衝突の条件 → チェイン法 → 線形探索法 → 負荷率と再ハッシュ → TypeScript実装 → 操作ラボ

01|そもそもハッシュ表は何に使うのですか

ハッシュ表は、キーから値をすばやく探したい場面で使われます。例えば、会員番号から会員情報を探す、商品コードから在庫情報を探す、URLからキャッシュ済みのレスポンスを探す、といった場面です。

配列やリストを先頭から順番に探すと、件数が増えるほど時間がかかります。一方、ハッシュ表は「どこを見ればよいか」をハッシュ関数で先に絞れるため、平均的には非常に高速です。

会員番号から会員情報を素早く探すイメージ
手元のキー会員番号 1051検索したい人を表す番号だけを持っていますハッシュ関数h(x) = x mod 101051 mod 10 = 1ハッシュ表(配列)0...1会員番号1051 → 田中さん2...3...
項目意味押さえたいポイント
会員管理会員番号 → 会員情報番号から目的の情報をすぐ取り出したい場面です。
辞書型データキー文字列 → 値JavaScriptのMapやオブジェクトをイメージすると分かりやすいです。
キャッシュURLや問い合わせ条件 → 結果同じ入力なら前回の結果をすぐ返したい場面で役立ちます。

02|ハッシュ関数は『どこを見るか』を決めます

ハッシュ表では、キーを入力して格納先の添字を返す関数をハッシュ関数と呼びます。今回の問題で使われているのは、最も基本的なh(x) = x mod nです。

ここで、x はキー、n はハッシュ表の大きさです。mod は余りを求める演算なので、結果は 0 から n−1 の範囲に収まります。つまり、そのまま配列の添字として使いやすいです。

項目意味押さえたいポイント
27 mod 12327 は bucket 3 へ格納します。
51 mod 12351 も bucket 3 へ向かうので衝突します。
20 mod 12820 は bucket 8 へ格納します。
余りが同じなら同じバケットへ集まります
キーh(x) = x mod 12バケット2727 mod 12 = 3bucket 35151 mod 12 = 3bucket 388 mod 12 = 8bucket 82020 mod 12 = 8bucket 8

03|シノニムとは何ですか

異なるキーなのに、ハッシュ関数を通すと同じ値になることがあります。このとき、そのキー同士をシノニムと呼びます。一般的には衝突(collision)という言い方もよく使います。

例えば 27 と 51 は違う数ですが、12 で割った余りはどちらも 3 です。したがって、h(x) = x mod 12では同じ bucket 3 に対応します。

04|なぜ『差が n の倍数』だと衝突するのですか

2つのキー a、b が同じ余りを持つとき、a と b は次のように書けます。

同じ余りを持つ2つの整数
a = pn + r
b = qn + r
-----------------
a - b = (p - q)n

右辺は n の整数倍なので、左辺である a−b も n の倍数です。逆に、a−b が n の倍数なら、a と b を n で割った余りは同じになります。したがって、次の関係が成り立ちます。

衝突の必要十分条件

a mod n = b mod n ⇔ a − b が n の倍数

例えば a=51、b=27、n=12 なら、51−27=24 であり 12 の倍数です。したがって、この2つは衝突します。試験問題の正答が「a−b が n の倍数」になる理由はここです。

05|合同式で見るとさらに短く書けます

数学では、余りが同じであることを合同と呼び、a ≡ b (mod n)と書きます。これは「a−b は n で割り切れる」という意味です。

同じ bucket 3 に集まるキーの例
3, 15, 27, 39, 51, 63, ...
→ いずれも 12 で割った余りは 3 です。

この見方に慣れておくと、ハッシュ表だけでなく暗号や数論の問題でも役立ちます。

06|衝突したらどう保存するのですか

ハッシュ表は衝突を前提に設計します。つまり、「ぶつかったときにどう処理するか」まで含めてデータ構造です。代表的な方法として、チェイン法とオープンアドレス法があります。オープンアドレス法の中でも基本なのが線形探索法です。

衝突したときの2つの代表的な対処方法
チェイン法同じ番地の箱の中に並べて入れます32751検索時は bucket 3 にある列の中を順番に見ます27 と 51 が同じ余りでも両方保存できます線形探索法ぶつかったら次の空き番地へ進みます3274515空51 も本来は 3 へ行きたいですが、3 が埋まっているため次の 4 を調べて格納します

07|チェイン法は『同じ箱の中に並べる』方法です

チェイン法では、同じバケットに来た要素をリストや配列にまとめて格納します。例えば bucket 3 に 27 と 51 が来たら、bucket 3 の中に [27, 51] のように並べます。

検索するときは、まずハッシュ値で bucket 3 を求め、その中を順番に見ていきます。実装が分かりやすく、表が埋まっても追加しやすいのが利点です。

項目意味押さえたいポイント
追加同じバケットの列に入れます衝突しても保存しやすいです。
検索対応する列の中を順に比較しますそのバケットに要素が集中すると遅くなります。
削除列から対象だけを取り除きますオープンアドレス法より扱いやすい場合があります。

08|線形探索法は『次の空き番地へ進む』方法です

線形探索法では、衝突したら次の番地、その次の番地、というように順番に空きを探します。例えば 27 と 51 がどちらも 3 を指しているとき、27 を 3 に入れた後で 51 を入れようとすると、3 は埋まっているので 4 を調べます。

線形探索法のイメージ
51 mod 12 = 3
bucket 3 は使用中
→ bucket 4 を調べます
→ 空いていればそこへ格納します

配列だけで完結するため実装しやすい反面、使用済みの領域が連続すると探索回数が増えることがあります。これをクラスタリングと呼びます。

09|検索時の流れを具体的に追ってみましょう

ハッシュ表は「一発で見つかる」ように見えますが、実際には次のような手順を踏んでいます。衝突がなければ非常に短い手順ですが、衝突があるとバケット内の比較や次の番地の確認が加わります。

検索時に何をしているのかを言葉で追う図

STEP 1

キーを受け取ります

例:会員番号 51 を探したいです。

STEP 2

ハッシュ値を計算します

51 mod 12 = 3 なので、まず bucket 3 を見ます。

STEP 3

衝突処理に従って確認します

チェイン法なら列を順に比較し、線形探索法なら次の番地を調べます。

10|負荷率が高いと遅くなりやすいです

ハッシュ表の性能を考えるときは、負荷率(load factor)も重要です。これは、登録要素数 m をバケット数 n で割った値で、α = m / nと表します。

負荷率が大きいほど、同じバケットに要素が集まりやすくなります。チェイン法では1つのバケットの列が長くなり、線形探索法では空きが見つかりにくくなります。そのため、実装では一定以上になったら表を大きくして再ハッシュすることがあります。

11|TypeScriptで基本形を確認します

まずはチェイン法によるシンプルな実装例です。同じハッシュ値になったキーは、同じバケット配列の中へ入れます。

チェイン法の基本実装例
class IntegerHashTable {
  private buckets: number[][];

  constructor(private readonly size: number) {
    this.buckets = Array.from({ length: size }, () => []);
  }

  private hash(key: number): number {
    return ((key % this.size) + this.size) % this.size;
  }

  add(key: number): void {
    const bucket = this.buckets[this.hash(key)];
    if (!bucket.includes(key)) {
      bucket.push(key);
    }
  }

  has(key: number): boolean {
    return this.buckets[this.hash(key)].includes(key);
  }
}

ここでは整数キーだけを扱っています。実際には文字列キーを数値へ変換するハッシュ関数を用意してから配列へ格納する場合も多いです。

12|操作ラボで『ぶつかり方』を目で確認しましょう

次のラボでは、バケット数、キー、格納方式を変えながら、衝突の起こり方を確認できます。特に、同じ余りなら同じ場所へ向かうことと、チェイン法と線形探索法で保存のされ方が違うことに注目して試してみてください。

INTERACTIVE LAB

ハッシュ表の衝突を目で追えるラボ

会員番号を棚へ入れるイメージで試してください。どのキーも、まず key mod n で最初の候補番地が決まります。そこが埋まっていたときに、同じ箱へ入れるのがチェイン法、次の空き番地へ進むのが線形探索法です。

登録キー数

4

衝突した挿入

2 件

負荷率 m / n

0.33

初期状態では、27 と 51 はどちらも余り 3、8 と 20 はどちらも余り 8 になります。まず同じ余りが同じ場所へ向かうことを確認してください。

番地格納されたキー
0
空き
1
空き
2
空き
3
2751
4
空き
5
空き
6
空き
7
空き
8
820
9
空き
10
空き
11
空き

最初に向かう番地:3

キー 51 は見つかりました。

実際に格納されている番地:3(調べたスロット数:1)

挿入履歴

キー最初の番地格納番地状態
2733衝突なし
5133衝突あり
888衝突なし
2088衝突あり

試し方のヒント

  • まず 27 と 51 がどちらも 3 に向かうことを確かめてください。
  • 次に方式をチェイン法と線形探索法で切り替え、保存される場所の違いを見てください。
  • バケット数 n を変えると余りが変わるので、衝突の起こり方も変わります。

13|まとめ

ハッシュ表は、キーから格納先をすばやく求めるためのデータ構造です。今回の問題のようにh(x) = x mod nを使う場合、a−b が n の倍数なら a と b は同じ余りを持ち、衝突します。

そのうえで、衝突時にはチェイン法なら同じ箱の中へ並べ、線形探索法なら次の空き番地へ進みます。数式だけでなく、用途と保存のイメージを合わせて理解しておくと、試験でも実務でも忘れにくくなります。