การแฮช (Hashing)
| English | ไทย |
|---|---|
| hash function/hæʃ ˈfʌŋkʃn/ | ฟังก์ชันแฮช |
| key/kiː/ | คีย์ |
| address/əˈdres/ | ที่อยู่ |
| deterministic/dɪˌtɜːmɪˈnɪstɪk/ | deterministic |
| collision/kəˈlɪʒn/ | การชนกัน |
| linear probing/ˈlɪnɪə ˈprəʊbɪŋ/ | การสำรวจเชิงเส้น |
| chaining/ˈtʃeɪnɪŋ/ | การเชื่อมโยง |
| load factor/ləʊd ˈfæktə/ | ปัจจัยการโหลด |
การหาแถวหนึ่งในห้าสิบล้านโดยไม่ดู
- registers ห้างสรรพสินค้าสแกน barcode และราคาปรากฏก่อนมือคุณออกจากสินค้า ไฟล์ผลิตภัณฑ์มีห้าสิบล้านบรรทัด
- ไม่มีใครค้นหาพวกมัน หมายเลขบาร์โค้ดผ่านการคำนวณสั้นๆ ที่สร้างตำแหน่งขึ้น และคอมพิวเตอร์อ่านตำแหน่งนั้น: อ่านครั้งเดียว เปรียบเทียบไม่ได้ ใช้เวลาเท่าเดิมไม่ว่าไฟล์จะมีแถวห้าสิบแถวหรือห้าสิบล้านแถว
- การคำนวณนี้คือ hash function และแนวคิดทั้งหมดขึ้นอยู่กับมัน: อย่าเก็บข้อมูลในที่它将 fit, เก็บมันในที่ key ของมันบอกว่าเป็นเจ้าของ
- บทเรียนนี้คือ algorithms hashing, เกิดอะไรขึ้นเมื่อสอง keys ต้องการ slot เดียวกัน,以及如何 search และ insert
Hash function
- Hash function หรือ hashing algorithm รับ key ของเรคอร์ดและผลิต address ที่เรคอร์ดถูกจัดเก็บ
- ตัวที่ดีควร fast, deterministic (key เดียวกันให้ address เหมือนกันเสมอ) และกระจาย keys evenlyAcross the available slots
- สำหรับช่อง $N$ หลักสูตรคาดหวัง 3 วิธี: โมดูลัส (modulo),
address ← key MOD N; การพับ (folding), แบ่งคีย์เป็นส่วน แล้วบวกกัน จากนั้น MOD $N$; แฮชสตริง (string hash), บวกค่าโค้ดของตัวอักษรทั้งหมด แล้ว MOD $N$
ฟังก์ชันแฮช:
ฟังก์ชันแฮชแมปคีย์เข้ากับที่อยู่ ทำให้สามารถค้นหาด่วนได้เกือบทันที
อะไรทำให้ฟังก์ชันแฮชดี? เลือก ทั้งหมด ที่เกี่ยวข้อง
รวดเร็ว เป็นค่าคงที่ และกระจายอย่างสม่ำเสมอ ไม่มีฟังก์ชันแฮชที่สมจริงใดจะหลีกเลี่ยงการชนได้โดยสิ้นเชิง จึงเป็นเหตุผลว่าทำไมทุกการออกแบบจึงมีกลยุทธ์การแก้ไข
ตัวอย่างทำแบบฝึกหัด: ใช้แต่ละอัลกอริทึม
- ไฟล์มี 10 ช่อง หมายเลข 0 ถึง 9 คีย์ 4517 จะไปอยู่ที่ไหน?
- โมดูลัส: $4517 \bmod 10 = 7$, ดังนั้นอยู่ที่ช่อง 7
- การพับ คู่ละสองหลัก: $45 + 17 = 62$, แล้ว $62 \bmod 10 = 2$, ดังนั้นอยู่ที่ช่อง 2
- และคีย์ "CAB" โดยใช้แฮชสตริง? $67 + 65 + 66 = 198$, แล้ว $198 \bmod 10 = 8$, ดังนั้นอยู่ที่ช่อง 8
- แสดงการคำนวณ คะแนนให้ที่วิธีคิด ไม่ใช่แค่เลขช่อง
การใช้ฟังก์ชันแฮชแบบโมดูล address ← key MOD N ด้วยคีย์ = 27 และ N = 10 จะได้ที่อยู่ใด?
27 MOD 10 = 7 (เศษเหลือเมื่อหาร 27 ด้วย 10)
ตารางมี 10 ช่อง ใช้เทคนิค folding แบบจับคู่กับคีย์ 4517 (นำ 45 บวกกับ 17 แล้ว MOD 10) จะไปอยู่ที่ช่องใด?
45 + 17 = 62, และ 62 MOD 10 = 2 หากใช้ฟังก์ชันแฮชแบบโมดูลกับคีย์เดียวกันจะไปที่ช่อง 7 แทน
การชนกัน (Collisions)
- การชนกัน เกิดขึ้นเมื่อคีย์ที่แตกต่างกันสองตัวแฮชไปที่ ที่อยู่เดียวกัน ในระบบแฮชใดๆ และไฟล์จริงใด การชนกันเป็นเรื่องแน่นอน ดังนั้นกลยุทธ์การจัดการจึงเป็นส่วนหนึ่งของการออกแบบตั้งแต่ต้น ไม่ใช่สิ่งที่มาคิดทีหลัง
- Linear probing จะบันทึกข้อมูลลงใน ช่องว่างถัดไป โดยวนกลับไปยังจุดเริ่มต้นเมื่อถึงท้ายตาราง ทำง่ายแต่ข้อมูลจะ รวมกลุ่ม (cluster): พื้นที่เต็มจะขยายออก และทุกคีย์ที่ตกลงในพื้นที่นี้จะใช้เวลาค้นหานานขึ้น
- Chaining ทำให้แต่ละช่องเป็นหัวของ ลิสต์เชื่อมโยง (linked list) ของบันทึกทั้งหมดที่แฮชมาที่ช่องนั้น ไม่มีการรวมกลุ่ม แต่ใช้หน่วยความจำเพิ่มสำหรับพอยน์เตอร์และต้องเดินตามลิสต์เล็กน้อย
- Rehashing ใช้ฟังก์ชันแฮชที่สองเพื่อหาช่องอื่น กระจายคีย์ได้ดีขึ้นแต่เสียเวลาในการคำนวณมากขึ้น
การชนเกิดขึ้นเมื่อ:
คีย์สองตัวที่แมปไปยังช่องเดียวกันคือการชน ต้องถูกแก้ไขด้วยการค้นหา (probing), การเชื่อม (chaining) หรือการแฮชใหม่ (rehashing)
จับคู่แนวคิดการจัดการกับการชนแต่ละอันกับสิ่งที่มันทำ
การชนถูกแก้ไขด้วยการเชื่อมหรือการหา; การรักษาค่าปัจจัยการโหลดให้ต่ำช่วยให้การค้นหาอยู่ใกล้ O(1)
การค้นหาและการแทรก
- การแทรก: แฮชคีย์ หากช่องว่าง ให้บันทึกข้อมูลลง หากไม่ว่าง ให้ทำตามกลยุทธ์แก้ไข คือช่องว่างถัดไปสำหรับ linear probing หรือหน้าสุดของลิสต์ในช่องนั้นสำหรับ chaining
- การค้นหา: แฮชคีย์แล้วอ่านช่องนั้น หากคีย์ที่เก็บไว้ตรงกัน พบบันทึกแล้ว หากไม่ตรง ให้ทำตามกลยุทธ์เดิม จนกว่าคีย์จะตรงหรือพบ ช่องว่าง ซึ่งพิสูจน์ว่าไม่มีบันทึกนี้อยู่ในไฟล์
- ทั้งสองกระบวนการใช้ กลยุทธ์เดียวกัน การค้นหาที่หยุดทันทีเมื่อคีย์ไม่ตรงจะพลาดบันทึกที่ถูกย้ายตำแหน่งจากการชนกันไปแล้วทั้งหมด
ตัวอย่างทำแบบฝึกหัด: ติดตามการชนกัน
- ตารางขนาด 10 ช่อง ใช้
key MOD 10พร้อม linear probing แทรก 23, 33, 43 ตามลำดับ แล้วค้นหา 43 - 23 แฮชไปที่ 3; ช่อง 3 ว่าง จึงวางไว้ที่นั่น 33 แฮชไปที่ 3; ช่อง 3 ถูก 23用的, ดังนั้น linear probing วางไว้ในช่อง 4 43 แฮชไปที่ 3; ช่อง 3 และ 4 ถูกใช้แล้ว จึงวางไว้ในช่อง 5
- ค้นหา 43: แฮชไปที่ 3 อ่านช่อง 3 คีย์คือ 23 ไม่ตรง จึงprobe ต่อ; ช่อง 4 มี 33 ไม่ตรง; ช่อง 5 มี 43 พบแล้ว หลังอ่านทั้งหมดสามครั้ง
- ช่วงยาวสามช่องนี้คือการรวมกลุ่มที่ linear probing ก่อให้เกิด
แฮชแต่ละคีย์ตรงไปยังถัง (bucket)
ฟังก์ชันแฮชแปลงคีย์ให้เป็นเลขถัง เพื่อให้คุณข้ามไปหาเรคอร์ดโดยตรงแทนการค้นหา เมื่อคีย์สองตัวตกลงในถังเดียวกัน นั่นคือการชนกัน — พวกเขาจะเชื่อมต่อกันในถังนั้น
เมื่อค้นหาในตารางแฮช เรคอร์ดนั้นไม่มีอยู่ในไฟล์ทันทีที่ช่องแรกที่ถูกอ่านมีคีย์ต่างออกไป
เรคอร์ดอาจถูกย้ายออกจากการชน การค้นหาลงตามกลยุทธ์การแก้ไขเดียวกันจนกว่าจะพบความตรงกันหรือช่องว่าง
ปัจจัยโหลด (Load factor)
- ปัจจัยโหลด คือจำนวนบันทึกหารด้วยจำนวนช่อง เป็นตัวเลขเดียวที่ใช้ทำนายประสิทธิภาพของตาราง
- ต่ำกว่าประมาณ 70% การค้นหาค่าเฉลี่ยจะอยู่ใกล้การอ่านเพียงครั้งเดียว เหนือจากนี้ ลำดับการ probe จะยาวขึ้นอย่างมากและประสิทธิภาพจะลดลงสู่การค้นหาแบบเชิงเส้น
- ทางแก้คือทำให้ตารางใหญ่ขึ้นและ rehash บันทึกทุกอย่างเข้าไปใหม่ นี่คือเหตุผลที่ตารางแฮชถูกกำหนดขนาดตามข้อมูลที่มันจะรองรับในอนาคต ไม่ใช่ข้อมูลที่มีอยู่ในปัจจุบัน
ตารางขนาด 10 ช่องใช้ key MOD 10 พร้อม linear probing หลังเพิ่ม 23, 33 และ 43 ตามลำดับ ช่องใดที่เก็บ 43?
ทั้งหมดแฮชไปที่ 3 23 ไปที่ช่อง 3, 33 ถูกหาไปที่ 4, และ 43 ไปที่ 5 สามคีย์ติดกันนี้คือสิ่งที่ linear probing ก่อให้เกิดการรวมกลุ่ม (clustering) พอดี
คะแนนที่หลุดหายไป
- การชนกันคือ คีย์สองตัว ที่ที่อยู่หนึ่ง มันไม่ใช่ข้อผิดพลาดและไม่มีการสูญเสียข้อมูล; มันเป็นสถานการณ์ปกติที่กลยุทธ์จะจัดการได้
- การค้นหาต้องใช้ กลยุทธ์ แก้ไขที่เหมือนกันกับการแทรก และหยุดเฉพาะเมื่อเจอคีย์ตรงหรือพบ ช่องว่าง
- Linear probing รวมกลุ่ม; Chaining ใช้ หน่วยความจำ ให้บอกข้อดีข้อเสีย ไม่ใช่แค่กลไกการทำงาน
- ปัจจัยโหลดคือบันทึก หารด้วย ช่อง, และเกณฑ์คือประมาณ 70%, ไม่ใช่ 100%
เพื่อให้การค้นหาในตารางแฮชเร็ว ปัจจัยการโหลด (เรคอร์ด ÷ ช่อง) ควรถูกเก็บไว้:
ปัจจัยการโหลดที่ต่ำลงหมายถึงการชนน้อยลง ดังนั้นการค้นหาจึงยังคงอยู่ใกล้ O(1)
คุณเข้าใจแล้ว
- ฟังก์ชันแฮช แปลง คีย์ เป็น ที่อยู่: เร็ว, เป็นระบบ, กระจายสม่ำเสมอ; โมดูลัส, การพับ และแฮชสตริง เป็นสามสิ่งที่ต้องรู้
- การชนกัน คือคีย์สองตัวแฮชไปที่ที่อยู่หนึ่ง แก้ไขได้ด้วย linear probing (ช่องว่างถัดไป, รวมกลุ่ม), chaining (ลิสต์เชื่อมโยงต่อช่อง, ใช้หน่วยความจำ) หรือ rehashing
- การแทรกและการค้นหาใช้กลยุทธ์เดียวกัน; การค้นหาจบเมื่อเจอคีย์ตรงหรือช่องว่าง
- รักษา ปัจจัยโหลด (บันทึกหารด้วยช่อง) ไว้ต่ำกว่าประมาณ 70% เพื่อการค้นหาค่าเฉลี่ยหนึ่งครั้ง