10.1 ハッシュ関数とハッシュテーブルの導入
ハッシュ関数とテーブルの理解を深めてみよう。ハッシュ関数は、任意の長さの入力データを固定長の文字列に変換するアルゴリズムだよ。基本的な特性は:
- 決定性: 同じ入力データは常に同じ結果を出す。
- 高速計算: ハッシュは迅速に計算されるべき。
- 不可逆性 (暗号学的ハッシュ関数の場合): ハッシュから元のデータを復元するのは不可能 (または非常に困難)。
- 均等な分布: 入力データの小さな変更がハッシュに大きな変化をもたらす。
簡単に言うと、同じオブジェクトのハッシュ関数は必ず一致するけど、2つのオブジェクトのハッシュ関数が一致しても、それが同じオブジェクトとは限らない。数学ではこれを必要十分条件とは呼ばないんだ。
ハッシュテーブルは、情報を効率的に保存し検索するためにハッシュ関数を使用するデータ構造だよ。それは以下で構成されている:
- データを保存するための"バケット"の配列。
- データをどのバケットに配置するかを決定するハッシュ関数。
ハッシュテーブルは通常、平均してO(1)の計算量でデータに迅速にアクセスできるようにする。
ハッシュ関数の実生活での応用
例: ブロックチェーン技術
ブロックチェーンでは、ハッシュ関数はブロックのユニークな識別子を作成し、データの整合性を確保するために使用される。各ブロックは前のブロックのハッシュを含んでおり、チェーンを形成してシステムを変更に強くする。
import hashlib
import time
class Block:
def __init__(self, data, previous_hash):
self.timestamp = time.time()
self.data = data
self.previous_hash = previous_hash
self.hash = self.calculate_hash()
def calculate_hash(self):
hash_string = str(self.timestamp) + str(self.data) + str(self.previous_hash)
return hashlib.sha256(hash_string.encode()).hexdigest()
# 使用例
block1 = Block("トランザクション 1", "0")
block2 = Block("トランザクション 2", block1.hash)
print(f"ブロック1のハッシュ: {block1.hash}")
print(f"ブロック2のハッシュ: {block2.hash}")
パフォーマンス比較
配列内の重複を見つける問題を考えてみよう。ハッシュテーブルを使用した解法と使わない解法を比較しよう:
import time
def find_duplicates_with_hash(arr):
seen = set()
duplicates = []
for item in arr:
if item in seen:
duplicates.append(item)
else:
seen.add(item)
return duplicates
def find_duplicates_without_hash(arr):
duplicates = []
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] == arr[j] and arr[i] not in duplicates:
duplicates.append(arr[i])
return duplicates
# パフォーマンステスト
arr = list(range(10000)) + list(range(5000)) # 重複のある配列
start = time.time()
find_duplicates_with_hash(arr)
end = time.time()
print(f"ハッシュテーブル使用時の実行時間: {end - start} 秒")
start = time.time()
find_duplicates_without_hash(arr)
end = time.time()
print(f"ハッシュテーブル未使用時の実行時間: {end - start} 秒")
プログラムを実行して、特に大きなデータセットでハッシュテーブルの使用がどれだけ重複検索を加速するかを確認してみよう。
10.2 ハッシュ関数の実際の課題での応用例
1. パスワードのハッシュ化
ハッシュ関数はパスワードを安全に保存するために使われる。パスワードを平文で保存する代わりに、システムはそのハッシュを保存する。ユーザーがパスワードを入力すると、システムは入力されたパスワードをハッシュ化し、データベース内のハッシュと比較する。
実装例:
import hashlib
def hash_password(password):
return hashlib.sha256(password.encode()).hexdigest()
# 使用例:
password = "securepassword"
hashed_password = hash_password(password)
print(f"パスワードのハッシュ: {hashed_password}")
2. データの整合性チェック
ハッシュ関数はファイルやデータの整合性をチェックするために使われる。例えば、ファイルが転送中に変更や破損していないかを確認するため。
実装例:
import hashlib
def get_file_hash(file_path):
hasher = hashlib.sha256()
with open(file_path, 'rb') as file:
buf = file.read()
hasher.update(buf)
return hasher.hexdigest()
# 使用例:
file_hash = get_file_hash('example.txt')
print(f"SHA-256ファイルのハッシュ: {file_hash}")
3. 検索エンジンとインデックス化
検索エンジンはインデックスを作成し、情報を迅速に検索するためにハッシュ関数を使用する。各ドキュメントはキーワードごとにインデックス化され、ハッシュ関数は指定された単語を含むドキュメントを迅速に見つける手助けをする。
実装例:
def create_index(text):
index = {}
words = text.split()
for word in words:
word_hash = hash(word)
if word_hash not in index:
index[word_hash] = []
index[word_hash].append(word)
return index
# 使用例:
text = "This is an example text for indexing"
index = create_index(text)
print(f"インデックス: {index}")
10.3 ハッシュ関数を使用した検索の最適化
1. 重複検索におけるハッシュテーブルの使用
ハッシュテーブルは、要素のハッシュ値を比較することで、配列内の重複を迅速に見つけることを可能にする。
実装例:
def find_duplicates(arr):
seen = set()
duplicates = []
for item in arr:
if item in seen:
duplicates.append(item)
else:
seen.add(item)
return duplicates
# 使用例:
arr = [1, 2, 3, 2, 4, 5, 6, 4, 7]
print(find_duplicates(arr)) # 出力: [2, 4]
2. 指定された合計のペアを検索する最適化
ハッシュテーブルは、配列内で合計が指定された値に等しい数字のペアを効率的に見つけることができる。
実装例:
def find_pairs_with_sum(arr, target_sum):
seen = set()
pairs = []
for num in arr:
complement = target_sum - num
if complement in seen:
pairs.append((complement, num))
seen.add(num)
return pairs
# 使用例:
arr = [2, 4, 3, 7, 8, -2, 10, -1]
target_sum = 6
print(find_pairs_with_sum(arr, target_sum)) # 出力: [(4, 2), (3, 3), (8, -2)]
10.4 様々なアルゴリズムにおけるハッシュ関数の使用例
1. テキスト内の単語の数をカウント
ハッシュテーブルを使用して、テキスト内の各単語の出現頻度をカウントしましょう。
実装例:
def count_words(text):
word_count = {}
words = text.split()
for word in words:
if word in word_count:
word_count[word] += 1
else:
word_count[word] = 1
return word_count
# 使用例:
text = "this is a test this is only a test"
print(count_words(text)) # 出力: {'this': 2, 'is': 2, 'a': 2, 'test': 2, 'only': 1}
2. 2つの配列の交差を確認する
2つの配列が交差しているかどうか(少なくとも1つの共通要素があるか)を確認しましょう。
実装例:
def has_intersection(arr1, arr2):
set1 = set(arr1)
for item in arr2:
if item in set1:
return True
return False
# 使用例:
arr1 = [1, 2, 3, 4]
arr2 = [3, 5, 6, 7]
arr3 = [8, 9, 10]
print(has_intersection(arr1, arr2)) # 出力: True
print(has_intersection(arr1, arr3)) # 出力: False
3. 配列内の要素のユニーク性を確認する
配列にユニークな要素のみが含まれているかどうかを確認しましょう。
実装例:
def all_unique(arr):
seen = set()
for item in arr:
if item in seen:
return False
seen.add(item)
return True
# 使用例:
arr1 = [1, 2, 3, 4, 5]
arr2 = [1, 2, 3, 4, 5, 3]
print(all_unique(arr1)) # 出力: True
print(all_unique(arr2)) # 出力: False
GO TO FULL VERSION