edubun
Bahasa Indonesia

Tabel hash

Dengan 47 disimpan dalam tabel berisi 7 bucket, komputer langsung menuju bucket 47 mod 7 = 5 alih-alih mencari di seluruh tabel.

Mengubah kunci menjadi posisi

Sejauh ini, mencari nilai berarti menelusuri: paling banyak n langkah pada array yang tidak terurut atau linked list. Padahal array bisa mencapai posisi mana pun dalam satu langkah. Bagaimana kalau kuncinya sendiri yang memberi tahu posisinya? Kalau kunci-kuncimu bilangan cacah kecil, misalnya nomor siswa dari 0 sampai 99, kamu bisa menyimpan array berisi 100 sel dan menaruh siswa k di sel k. Catatan MIT menyebutnya direct access array (array akses langsung). Cara ini gagal kalau kemungkinan kuncinya sangat banyak, seperti nomor telepon atau kata, karena kamu tidak mampu menyediakan satu sel untuk setiap kunci yang mungkin.

Fungsi hash mengatasinya dengan memampatkan kunci apa pun ke rentang posisi yang kecil, 0 sampai m − 1. Fungsi sederhana untuk bilangan cacah adalah h(k) = k mod m, yaitu sisa pembagian oleh m. Array berisi m sel itu adalah tabel hash, dan sel-selnya sering disebut bucket (wadah). Untuk menyimpan 47 dengan m = 7, hitung 47 mod 7 = 5 lalu taruh di bucket 5. Untuk mencari 47 nanti, hitung hal yang sama dan lihat hanya di bucket 5.

Tabrakan dan chaining

Ada masalah. Dengan 7 bucket dan lebih dari 7 kemungkinan kunci, sebagian kunci pasti berbagi bucket: 12, 33, dan 47 semuanya bersisa 5. Ini disebut tabrakan (collision), dan catatan MIT menunjukkan bahwa tabrakan tidak bisa dihindari, menurut prinsip sarang merpati. Obat yang biasa adalah chaining (perantaian): setiap bucket menyimpan list kecil, yaitu rantai, berisi setiap kunci yang di-hash ke sana. Mencari 47 lalu memakan 1 langkah untuk hash ditambah satu perbandingan untuk setiap kunci yang diperiksa di rantai 5. Kalau 12 dan 33 ada di depannya, jadinya 1 + 3 = 4 langkah.

Seberapa panjang rantainya? Kalau kuncinya tersebar merata, setiap rantai berisi sekitar n/m kunci, bilangan yang disebut faktor muatan (load factor). Buat m kira-kira sebesar n, maka rantainya tetap pendek, sehingga mencari, menyisipkan, dan menghapus rata-rata memakan jumlah langkah yang konstan. Bahayanya adalah sebaran yang buruk. Kalau setiap kunci di-hash ke bucket yang sama, satu rantai memuat semua n kunci dan tabelnya tidak lebih baik daripada linked list. Di laboratorium, atur 7 bucket lalu sisipkan 14, 21, dan 28: semuanya mendarat di bucket 0.

Memuat

Seret untuk memutar. Gulir atau cubit untuk memperbesar.

Tabel hash berisi 7 bucket yang memuat 12, 25, 33, 47, dan 70, dengan h(k) = k mod 7. Pilih sebuah kunci lalu sisipkan, cari, atau hapus. Penghitung langkah menghitung 1 untuk hash ditambah 1 untuk setiap kunci yang dibandingkan di rantai.
Fungsi hash sungguhan mencampur semua bit sebuah kunci, dan pustaka yang dirancang dengan baik memilihnya agar data biasa tersebar merata. MIT 6.006 melangkah lebih jauh dan memilih fungsi hash secara acak dari sebuah keluarga, sehingga tidak ada kumpulan kunci tetap yang selalu buruk. k mod m yang sederhana di laboratorium dipilih karena kamu bisa memeriksanya dengan tangan.

Tabel hash di JavaScript

Kamu jarang menulis tabel hash sendiri, karena JavaScript punya Map dan Set. Spesifikasi ECMAScript menyatakan keduanya "harus diimplementasikan dengan tabel hash atau mekanisme lain yang rata-rata memberikan waktu akses sublinear terhadap banyaknya elemen". Dengan kata lain, map.get(key) dan set.has(x) harus lebih cepat daripada menelusuri seluruh koleksi. MDN menambahkan bahwa Map menyimpan kunci sesuai urutan penyisipan dan, tidak seperti objek biasa, tidak berisi kunci bawaan yang bisa bertabrakan dengan kuncimu.

const price = new Map();
price.set("apple", 1.2);
price.set("bread", 2.5);
console.log(price.get("bread")); // 2.5, tanpa menelusuri list
console.log(price.has("milk"));  // false
const seen = new Set([4, 8, 15]);
console.log(seen.has(8));        // true
Latar belakangGagasan hashing dianggap berasal dari Hans Peter Luhn dari IBM. Sejarah pencarian yang ditulis Donald Knuth mencatat bahwa pada Januari 1953 Luhn menulis memorandum internal IBM yang mengusulkan agar catatan data dimasukkan ke bucket bernomor, dengan chaining untuk catatan yang bertabrakan. Fungsi hash-nya bekerja pada angka: jumlahkan pasangan angka yang bersebelahan dan simpan angka terakhir setiap jumlahnya, jadi 31415926 menjadi 4548. Insinyur IBM lain yang menggarap IBM 701 punya gagasan yang sama pada waktu yang hampir bersamaan, dan uraian pertama yang diterbitkan datang dari Arnold Dumey pada tahun 1956.
Fungsi hashMengubah kunci menjadi nomor bucket dari 0 sampai m − 1, misalnya k mod m.
TabrakanDua kunci berbeda mendapat bucket yang sama. Tidak terhindarkan kalau kunci lebih banyak daripada bucket.
ChainingSetiap bucket menyimpan list berisi setiap kunci yang di-hash ke sana.
Faktor muatann/m, panjang rantai rata-rata.
Map dan SetKoleksi berkunci bawaan JavaScript, yang diwajibkan rata-rata lebih cepat daripada linear.
  1. Dengan h(k) = k mod 7, ke bucket mana 47 masuk?
  2. Tabel memuat 12, 25, 33, 47, 70 (disisipkan dengan urutan itu) dengan m = 7. Berapa langkah untuk mencari 47 (1 untuk hash ditambah kunci yang dibandingkan)?
  3. Tabel yang sama: berapa langkah untuk mencari 12?
  4. Tabel yang sama: 19 tidak tersimpan. Berapa langkah yang dibutuhkan pencarian 19?
  5. Tabel yang sama: berapa faktor muatan n/m? (dua tempat desimal)
  6. Apa itu tabrakan?
    • Kunci yang terlalu besar
    • Dua kunci berbeda yang di-hash ke bucket yang sama
    • Menghapus sebuah kunci dua kali
    • Tabel tanpa kunci
  7. Mengapa tidak ada fungsi hash yang bisa menghindari tabrakan kalau kemungkinan kunci lebih banyak daripada bucket?
    • Komputer membulatkan bilangan
    • Prinsip sarang merpati
    • Kunci selalu genap
    • Rantainya terlalu pendek
  8. Kalau setiap kunci mendarat di bucket yang sama, bagaimana pencarian kunci berlangsung?
    • Tetap 1 langkah
    • Seperti menelusuri linked list berisi n kunci
    • Menjadi mustahil
    • Seperti pencarian biner
  9. Apa yang disyaratkan spesifikasi ECMAScript untuk waktu akses Map?
    • Tepat 1 langkah
    • Sublinear terhadap banyaknya elemen, rata-rata
    • Θ(n log n)
    • Tidak ada
  10. Hash Luhn tahun 1953 menjumlahkan pasangan angka yang bersebelahan dan menyimpan angka terakhir setiap jumlahnya. 31415926 menjadi apa?
Poin penting
  • Fungsi hash mengubah kunci menjadi nomor bucket; k mod m adalah salah satu yang sederhana.
  • Tabrakan tidak bisa dihindari; chaining menyimpan sebuah list di setiap bucket.
  • Dengan faktor muatan n/m yang dijaga tetap kecil, mencari, menyisipkan, dan menghapus rata-rata memakan langkah yang konstan.
  • Map dan Set di JavaScript harus rata-rata lebih cepat daripada linear.
Sumber kursus ini (11)
  • MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
  • lecture notes 2 Data Structures (interface vs data structure, Sequence and Set interfaces, stack = insert_last/delete_last, queue = insert_last/delete_first, array, linked list, dynamic array, amortized Θ(1) append)
  • 4 Hashing (chaining, pigeonhole collisions, chain length n/m)
  • 6 Binary Trees Part 1 (height, traversal order, O(h) operations) and 7 Binary Trees Part 2: AVL (Adelson-Velsky and Landis, 1962) at ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes
  • MDN Web Docs, Array.prototype.shift (shifts all values left by 1)
  • Array.prototype.unshift, Map (insertion order, Map vs Object, spec requires sublinear average access) and Set (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
  • ECMAScript 2025 Language Specification (tc39.es/ecma262)
  • 24.1 Map Objects and 24.2 Set Objects ("must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements")
  • Array.prototype.shift steps (Repeat, while k < len: move element k to k - 1)
  • D. E. Knuth, The Art of Computer Programming vol. 3 (2nd ed. 1998) section 6.4 History (H. P. Luhn, IBM internal memorandum January 1953, hashing with chaining; digit pairs added mod 10, 31415926 -> 4548) via gwern.net copy, and IEEE Spectrum, "Hans Peter Luhn and the Birth of the Hashing Algorithm" (2018)
  • "A brief history of the stack", SIGCIS (sigcis.org/files/A brief history.pdf: Bauer and Samelson applied for the patent in 1957, US 3,047,228 granted July 31, 1962; IPL list processing by Newell, Simon and Shaw at RAND 1956) and Wikipedia, Information Processing Language (Newell, Shaw, Simon, RAND and Carnegie Institute of Technology, about 1956).

Diperiksa: 2026-10-18