資格道場 › レッスン一覧 › アルゴリズムとプログラミング
アルゴリズムとプログラミング・10/13・13分

ハッシュ法と衝突の扱い

ゴール

ハッシュ法とは何かを他の検索方式と見分けられ、ハッシュ値の計算・衝突の判定・衝突時の格納位置のたどり方を、手を動かして自力で答えられるようになる。

ハッシュ法は「キーから一発で置き場所を計算する」ので、データが何件あってもほぼ一定の時間で探せます。試験では「定義を選ぶ問題」と「計算して位置を出す問題」が交互に、ほぼ同じ文面で繰り返し出ます。

このレッスンで稽古する(用語チェック・過去問)

結論:ハッシュ法は「キーを計算して置き場所を決める」方法

ハッシュ法とは、データのキー(会員番号など、探すときの目印になる値)をハッシュ関数(決まった計算式)に入れ、出てきた値(ハッシュ値)を格納場所(アドレス)として使う方法です。探すときも同じ計算をすれば置き場所がすぐに分かるので、表を端から順に見る必要がありません。

たとえるなら、ロッカーの番号を「誕生日の日にちの数字」で決めるルールです。15日生まれの人は15番のロッカーと決まっているので、名簿をめくらずに一発でたどり着けます。

前回の探索アルゴリズムと比べると、違いがはっきりします。

探索方法 探し方 探索時間
線形探索 先頭から順に見る データ数に比例して増える
2分探索 整列済みの表を半分ずつ絞る データ数が増えてもゆっくりしか増えない(log n)
ハッシュ法 キーを計算して置き場所に直行 表の大きさにほぼ関係なく一定(衝突が少なければ)
キー → 計算 → 置き場所に直行キー15日生まれハッシュ関数(決まった計算式)ハッシュ値15番1314151617ロッカー(格納場所)探すときも同じ計算で一発・端から見ない
キーをハッシュ関数で計算すると置き場所の番号が出るので、端から順に探さず目的の場所へ直行できる。

「ハッシュ法の説明はどれか」で混ぜられる方式

定義を問う問題では、別のアクセス方式や検索技術の説明がひっかけとして並びます。正解の決め手は「関数(アルゴリズム)で変換した値を格納アドレスにする」という言い回しです。

選択肢に出る説明 正体 ハッシュ法か
関数でキー値から格納アドレスを求める ハッシュ法 ○
各レコードに入っている「次のレコードのアドレス」をたどる ポインタによる連結(リスト構造) ×
キー値とアドレスの対応表を使う 索引(インデックス) ×
キー値をそのままアドレスにする 直接アドレス指定 ×
同義語・類義語のリストで検索漏れを防ぐ シソーラス ×
大量データから傾向や相関を見つける データマイニング ×
利用者の意図を判断する高度な検索 知識ベース・AI 的な検索 ×

「対応表を使う」と「計算で求める」の違いが最大のポイントです。ハッシュ法は表を引かずに計算するので速く、データが増えても扱いやすい(スケーラビリティが高い)と説明されます。

衝突(シノニム)は必ず起こりうる

違うキーなのに同じハッシュ値になることを衝突(コリジョン)といい、衝突したキー同士をシノニム(同義語)と呼びます。誕生日ロッカーの例なら、1月15日生まれと3月15日生まれは同じ15番になってしまいます。

格納場所の数よりキーの種類のほうがずっと多いのがふつうなので、衝突は避けられません。だから「衝突が発生しない方法である」という選択肢は必ず誤りです。衝突したときの処理方法には、大きく2種類あります。

方式 衝突したときの動き 覚え方
オープンアドレス法 表の中の別の空き場所を決まったルールで探して入れる 埋まっていたら隣(など)へずれる
チェイン法(連鎖法) 同じハッシュ値のデータをリストでつないで同じ場所にぶら下げる 同じ番地に数珠つなぎ

試験の計算問題で出るのはほぼオープンアドレス法のトレース(順に追いかけること)です。

違うキーが同じ番地に=衝突1月15日生まれ3月15日生まれ15番衝突(シノニム同士)オープンアドレス法15番:1月15日16番:3月15日埋まっていたら表の別の空きへずれるチェイン法15番1月15日3月15日同じ番地に数珠つなぎ
1月15日生まれと3月15日生まれが同じ15番になる衝突と、それをオープンアドレス法とチェイン法でどう収めるかの違い。

ハッシュ値の計算:問題の式をそのまま実行するだけ

ハッシュ値の計算問題は、問題文に書かれた変換ルールを1ステップずつ忠実に実行するだけです。知識より「手順を飛ばさない」ことが問われます。

よく出る道具は mod(割り算の余り)です。たとえば mod(15, 13) は「15 を 13 で割った余り」なので 2 です。余りは必ず 0〜(割る数−1) の範囲に収まるため、表の大きさで割れば必ず表の中の番号になります。

ほかに「キーを11進数とみなして10進数に直す」(基数変換法)のような変わったルールも出ますが、やることは同じです。どの桁を使うか・切り捨てるかを問題文のとおりにやれば解けます。

mod(15, 13)=15を13で割った余り13個でひと区切り(ここは捨てる)余り 2 → 2番0123456789101112余りは必ず0〜12=表の中の番号
mod(15, 13) は15個を13個ずつ区切った残りの2で、余りは必ず0〜12に収まるので表の中の番号になる。

衝突の判定:ASCII コードは「並び順の差」で解く

ASCII コードとは、文字を数値で表す代表的な文字コードです。小文字 a〜z には連続した番号が順に振られていて、a は 10 進で 97 です。

「1の位をハッシュ値にする」なら、1の位が同じになる組が衝突です。番号が連続しているので、アルファベット順で10文字離れた(差が10の倍数)文字同士が衝突します。a=97 を覚えていなくても、この性質だけで解けます。

1の位が同じグループ 例
a・k・u 97・107・117
b・l・v 98・108・118
c・m・w 99・109・119
d・n・x 100・110・120

オープンアドレス法のトレース:表を書いて1件ずつ埋める

トレース問題はメモ用紙に表(アドレスの欄)を書いて、データを1件ずつ順番に入れていくのが確実です。頭の中だけで追うと、途中で埋まった場所を見落とします。

注意点は2つです。1つは「最終アドレスの次は先頭に戻る」というルールがあるかどうか。もう1つは、ずらし方が「+1」だけとは限らず、「+1 がだめなら +4」のように問題ごとに独自のルールがあることです。

flowchart TD
  A["キーからハッシュ値を計算"] --> B{"その場所は空いている?"}
  B -- "はい" --> C["そこに格納"]
  B -- "いいえ(衝突)" --> D["ルールどおり次の候補へ(最後の次は先頭)"]
  D --> B

試験での問われ方

  • 定義問題:「関数で格納アドレスを求める」が正解。対応表=索引、次のアドレスをたどる=ポインタ、そのままアドレス=直接アドレス指定、と見分ける。
  • 特徴問題:「2分木を使う」「衝突しない」「表の大きさに比例する」はすべて誤り。
  • 計算問題:桁の和の mod、基数変換などを問題文どおりに計算する。商と余りの取り違えに注意。
  • 衝突判定:ASCII の1の位なら、アルファベットの距離が10の倍数かを見る。
  • トレース:表を書いて順に埋める。格納の順番を入れ替えないこと。
例題 1

桁の和を13で割った余りで位置を求める(54321)

10 進法で 5 桁の数 a1 a2 a3 a4 a5 を,ハッシュ法を用いて配列に格納したい。ハッシュ関数を mod(a1+a2+a3+a4+a5,13) とし,求めたハッシュ値に対応する位置の配列要素に格納する場合,54321 は配列のどの位置に入るか。ここで,mod(x,13) は,x を 13 で割った余りとする。
配列: 位置 0, 1, 2, …, 11, 12 の要素を持つ縦長の表(位置 0〜12 の 13 要素)

ア 1
イ 2
ウ 7
エ 11

出典:令和元年度 秋期 基本情報技術者試験 午前 問10

解き方と答えを見る
  1. ハッシュ関数は mod(a1+a2+a3+a4+a5, 13)。まず各桁を足す
  2. 5+4+3+2+1 = 15
  3. 15 を 13 で割る:15 ÷ 13 = 1 余り 2
  4. 余りの 2 がハッシュ値なので、位置 2 に格納する(商の 1 や和の 15 ではない)

答え:イ(位置 2)

例題 2

基数変換法でアドレスを求める(キー 55550)

0000 ~ 4999 のアドレスをもつハッシュ表があり,レコードのキー値からアドレスに変換するアルゴリズムとして基数変換法を用いる。キー値が 55550 のときのアドレスはどれか。ここでの基数変換法は,キー値を 11 進数とみなし,10 進数に変換した後,下 4 桁に対して 0.5 を乗じた結果(小数点以下は切捨て)をレコードのアドレスとする。

ア 0260
イ 2525
ウ 2775
エ 4405

出典:平成26年度 秋期 基本情報技術者試験 午前 問2

解き方と答えを見る
  1. 55550 を11進数とみなし、各桁に11のべき乗を掛ける。11^4=14641、11^3=1331、11^2=121、11^1=11、11^0=1
  2. 5×14641 = 73205
  3. 5×1331 = 6655
  4. 5×121 = 605
  5. 5×11 = 55
  6. 0×1 = 0
  7. 合計:73205+6655+605+55+0 = 80520(10進数)
  8. 下4桁を取り出すと 0520
  9. 0520 × 0.5 = 260(小数点以下は切り捨てだが、ここでは端数なし)
  10. 4桁で表すと 0260。アドレス範囲 0000〜4999 に収まっている

答え:ア(0260)

例題 3

ASCII コードの1の位で衝突する組を探す

キーが小文字のアルファベット 1 文字(a,b,…,z のいずれか)であるデータを,大きさが 10 のハッシュ表に格納する。ハッシュ関数として,アルファベットの ASCII コードを 10 進表記法で表したときの 1 の位の数を用いることにする。衝突が起こるキーの組合せはどれか。ASCII コードでは,昇順に連続した 2 進数が,アルファベット順にコードとして割り当てられている。

ア aとi
イ bとr
ウ cとl
エ dとx

出典:令和6年度 基本情報技術者試験 科目A 公開問題 問2

解き方と答えを見る
  1. 小文字は a=97 から連番。1の位が同じなら衝突する
  2. 連番なので、アルファベット順の距離が10の倍数なら1の位が一致する
  3. ア:a→i は 8 文字差(97 と 105 → 1の位 7 と 5)→衝突しない
  4. イ:b→r は 16 文字差(98 と 114 → 8 と 4)→衝突しない
  5. ウ:c→l は 9 文字差(99 と 108 → 9 と 8)→衝突しない
  6. エ:d→x は 20 文字差(100 と 120 → 0 と 0)→衝突する

答え:エ(d と x)

例題 4

「余り+1」で格納し、埋まっていたら次へ(アドレス1〜6)

格納アドレスが 1~6 の範囲の直接編成ファイルにおいて,次の条件でデータを格納した場合,アドレス 1 に格納されているデータのキー値はどれか。 〔条件〕 (1) キー値が 3,4,8,13,14,18 の順でデータを格納する。 (2) データのキー値を 5 で割った余りに 1 を加えた値を格納アドレスにする。 (3) 格納アドレスに既にデータがある場合には,次のアドレスに格納する。これを格納できるまで繰り返す。最終アドレスの次は先頭とする。 (4) 初期状態では,ファイルは何も格納されていない。

ア 8
イ 13
ウ 14
エ 18

出典:平成28年度 秋期 基本情報技術者試験 午前 問20

解き方と答えを見る
  1. ルール:アドレス = (キー mod 5) + 1。埋まっていたら次のアドレスへ。6 の次は 1 に戻る
  2. 3:3 mod 5 = 3 → 3+1 = 4。空き → アドレス4 に 3
  3. 4:4 mod 5 = 4 → 4+1 = 5。空き → アドレス5 に 4
  4. 8:8 mod 5 = 3 → 4。4 は埋まり → 5 も埋まり → 6 が空き → アドレス6 に 8
  5. 13:13 mod 5 = 3 → 4。4・5・6 が埋まり → 6 の次は先頭の 1 が空き → アドレス1 に 13
  6. 14:14 mod 5 = 4 → 5。5・6・1 が埋まり → 2 が空き → アドレス2 に 14
  7. 18:18 mod 5 = 3 → 4。4・5・6・1・2 が埋まり → 3 が空き → アドレス3 に 18
  8. アドレス1 に入っているのは 13

答え:イ(13)

例題 5

「+1 がだめなら +4」の独自ルールでたどる(A[0]〜A[9])

次の規則に従って配列の要素 A[0],A[1],… ,A[9] に正の整数 k を格納する。k として 16,43,73,24,85 を順に格納したとき,85 が格納される場所はどこか。ここで,x mod y は,x を y で割った剰余を返す。また,配列の要素は全て 0 に初期化されている。 〔規則〕 (1) A[k mod 10] = 0 ならば,k を A[k mod 10] に格納する。 (2) (1)で格納できないとき,A[(k+1) mod 10] = 0 ならば,k を A[(k+1) mod 10] に格納する。 (3) (2)で格納できないとき,A[(k+4) mod 10] = 0 ならば,k を A[(k+4) mod 10] に格納する。

ア A[3]
イ A[5]
ウ A[6]
エ A[9]

出典:平成25年度 秋期 基本情報技術者試験 午前 問7

解き方と答えを見る
  1. ルール:まず A[k mod 10]、埋まっていたら A[(k+1) mod 10]、それも埋まっていたら A[(k+4) mod 10]
  2. 16:16 mod 10 = 6 → A[6] が空き → A[6] に 16
  3. 43:43 mod 10 = 3 → A[3] が空き → A[3] に 43
  4. 73:73 mod 10 = 3 → A[3] は埋まり → (73+1) mod 10 = 4 → A[4] に 73
  5. 24:24 mod 10 = 4 → A[4] は埋まり → (24+1) mod 10 = 5 → A[5] に 24
  6. 85:85 mod 10 = 5 → A[5] は埋まり → (85+1) mod 10 = 6 → A[6] も埋まり → (85+4) mod 10 = 89 mod 10 = 9 → A[9] に 85

答え:エ(A[9])

要点(試験直前に読む)

  • ハッシュ法=キーをハッシュ関数で計算した値を格納アドレスにする方法。探索時間は表の大きさにほぼ関係しない
  • 衝突(シノニム)は必ず起こりうる。対処はオープンアドレス法(別の空きへ)とチェイン法(リストでつなぐ)
  • 計算問題は問題文の式どおりに実行。mod は「余り」、基数変換は桁ごとにべき乗を掛けて足す
  • ASCII の1の位で衝突判定するなら、アルファベット順で差が10の倍数の組が衝突(d と x など)
  • トレース問題は表を書いて1件ずつ埋める。「最後の次は先頭」や「+1 の次は +4」などの独自ルールを読み落とさない

ひっかけ

  • 「衝突が発生しない」「2分木を使う」「表の大きさに比例する」は、どれもハッシュ法の特徴として誤り
  • 「対応表を使う」は索引、「次のレコードのアドレスを使う」はポインタ、「キーをそのままアドレスに」は直接アドレス指定。ハッシュ法は「関数で求める」
  • シソーラス・データマイニング・知識ベースの説明が並んだら、「変換した値を格納アドレスにする」ものを選ぶ
  • mod の計算で商や和そのものを答えにしない。54321 なら和 15 でも商 1 でもなく余り 2
  • トレースで格納順を入れ替えたり、埋まった場所を見落としたりすると別の選択肢になる。必ず表に書き込みながら追う

用語(14)

ハッシュ法 はっしゅほう
キーをハッシュ関数で計算した値を格納場所として使い、表をなめずに直接アクセスする探索・格納の方法。
⚠ 索引(対応表を引く)や直接アドレス指定(キーをそのままアドレスにする)と混同しやすい。「関数で計算する」がハッシュ法の目印。
ハッシュ関数 はっしゅかんすう
キーを入力すると、格納場所の番号(ハッシュ値)を返す計算式。
ハッシュ値 はっしゅち
ハッシュ関数の計算結果で、格納場所のアドレスとして使う値。
⚠ 計算途中の値(桁の和や割り算の商)をハッシュ値と取り違えやすい。最後に出た余りなどが答え。
ハッシュ表 はっしゅひょう
ハッシュ値をアドレスとしてデータを格納する配列状の表。
衝突 しょうとつ(コリジョン:collision)
異なるキーなのに同じハッシュ値になり、同じ格納場所を指してしまうこと。
⚠ 「ハッシュ法は衝突が発生しない」は誤り。キーの種類が場所の数より多いので必ず起こりうる。
シノニム synonym(同義語)
ハッシュ値が同じになって衝突したキー同士のこと。
⚠ 検索技術のシソーラス(同義語・類義語リスト)とは別物。
オープンアドレス法 おーぷんあどれすほう(open addressing)
衝突したとき、決められたルールで表の中の別の空き場所を探して格納する方式。
⚠ チェイン法は同じ場所にリストでつなぐ方式。表の中で場所をずらすのがオープンアドレス法。
チェイン法 ちぇいんほう(連鎖法・chaining)
同じハッシュ値になったデータをリスト構造でつなぎ、同じ格納場所にまとめておく衝突処理方式。
⚠ オープンアドレス法は別の空き場所へずらす。チェイン法はずらさずにつなぐ。
mod modulo(剰余)
mod(x, y) は x を y で割った余りを返す演算で、結果は 0〜y−1 の範囲に収まる。
⚠ 割り算の商と取り違えやすい。mod は余り。
基数変換法 きすうへんかんほう
キーを別の基数(例:11進数)の数とみなして10進数に直し、その一部を加工してアドレスにするハッシュ関数の作り方。
ASCII コード American Standard Code for Information Interchange
英数字や記号を数値で表す代表的な文字コードで、小文字 a〜z は 97〜122 の連番。
直接編成ファイル ちょくせつへんせいふぁいる
キーから計算したアドレスにレコードを直接置き、直接読み書きするファイル編成で、ハッシュ法の考え方を使う。
⚠ 順にたどる順編成ファイルや、索引を使う索引順編成ファイルとは違う。
索引 さくいん(インデックス)
キー値と格納アドレスの対応表を用意し、それを引いてデータにアクセスする方式。
⚠ ハッシュ法は表を引かずに計算でアドレスを出す。「対応表」と書いてあれば索引。
直接アドレス指定 ちょくせつあどれすしてい
キー値そのものを格納アドレスとして使うアクセス方式。
⚠ ハッシュ法はキーを関数で変換してからアドレスにする。「そのまま」なら直接アドレス指定。

このレッスンの過去問

このレッスンで稽古する