TL; DR: ตารางแฮชรับประกันO(1)
ว่าจะเกิดกรณีเลวร้ายที่สุดหากคุณเลือกฟังก์ชันแฮชของคุณอย่างสม่ำเสมอโดยการสุ่มจากฟังก์ชันแฮชสากล กรณีที่เลวร้ายที่สุดที่คาดไว้ไม่เหมือนกับกรณีทั่วไป
ข้อจำกัดความรับผิดชอบ:ฉันไม่ได้พิสูจน์อย่างเป็นทางการว่าตารางแฮชO(1)
นั้นเป็นอย่างไรลองดูวิดีโอนี้จาก coursera [ 1 ] ฉันยังไม่ได้พูดคุยเกี่ยวกับลักษณะการตัดจำหน่ายของตารางแฮช นั่นคือมุมฉากของการอภิปรายเกี่ยวกับการแฮชและการชนกัน
ฉันเห็นความสับสนอย่างมากเกี่ยวกับหัวข้อนี้ในคำตอบและความคิดเห็นอื่น ๆ และจะพยายามแก้ไขบางส่วนในคำตอบยาว ๆ นี้
การให้เหตุผลเกี่ยวกับกรณีที่เลวร้ายที่สุด
การวิเคราะห์กรณีที่เลวร้ายที่สุดมีหลายประเภท การวิเคราะห์คำตอบส่วนใหญ่ในที่นี้ไม่ใช่กรณีที่เลวร้ายที่สุด แต่เป็นกรณีเฉลี่ย [ 2 ] การวิเคราะห์กรณีโดยเฉลี่ยมีแนวโน้มที่จะนำไปใช้ได้จริงมากกว่า บางทีอัลกอริทึมของคุณอาจมีอินพุตกรณีเลวร้ายที่สุดตัวหนึ่ง แต่ใช้งานได้ดีสำหรับอินพุตอื่น ๆ ทั้งหมดที่เป็นไปได้ Bottomline คือรันไทม์ของคุณขึ้นอยู่กับชุดข้อมูลที่คุณใช้งานอยู่
พิจารณา pseudocode ต่อไปนี้ของget
วิธีการของตารางแฮช ที่นี่ฉันสมมติว่าเราจัดการการชนกันโดยการผูกมัดดังนั้นแต่ละรายการของตารางจึงเป็นรายการ(key,value)
คู่ที่เชื่อมโยงกัน นอกจากนี้เรายังถือว่าจำนวนถังm
ได้รับการแก้ไข แต่O(n)
ที่n
เป็นจำนวนขององค์ประกอบในการป้อนข้อมูล
function get(a: Table with m buckets, k: Key being looked up)
bucket <- compute hash(k) modulo m
for each (key,value) in a[bucket]
return value if k == key
return not_found
ในฐานะที่เป็นคำตอบอื่น ๆ ที่มีออกแหลมวิ่งนี้เฉลี่ยและกรณีที่เลวร้ายO(1)
O(n)
เราสามารถร่างบทพิสูจน์เล็ก ๆ น้อย ๆ ได้โดยการท้าทายที่นี่ ความท้าทายมีดังนี้:
(1) คุณให้อัลกอริทึมตารางแฮชของคุณแก่ฝ่ายตรงข้าม
(2) ฝ่ายตรงข้ามสามารถศึกษาและเตรียมตัวได้นานเท่าที่เขาต้องการ
(3) ในที่สุดฝ่ายตรงข้ามจะให้ข้อมูลขนาดn
เพื่อให้คุณแทรกในตารางของคุณ
คำถามคือตารางแฮชของคุณอยู่บนอินพุตของฝ่ายตรงข้ามเร็วแค่ไหน?
จากขั้นตอนที่ (1) ฝ่ายตรงข้ามรู้ฟังก์ชันแฮชของคุณ ในขั้นตอนที่ (2) ฝ่ายตรงข้ามสามารถสร้างรายการn
องค์ประกอบที่เหมือนกันhash modulo m
ได้โดยการสุ่มคำนวณแฮชขององค์ประกอบจำนวนมาก จากนั้นใน (3) พวกเขาสามารถให้รายการนั้นแก่คุณได้ แต่ดูเถิดเนื่องจากn
องค์ประกอบทั้งหมดแฮชไปยังที่เก็บข้อมูลเดียวกันอัลกอริทึมของคุณจะใช้O(n)
เวลาในการสำรวจรายการที่เชื่อมโยงในที่เก็บข้อมูลนั้น O(n)
กี่ครั้งที่เราลองใหม่อีกครั้งความท้าทายไม่มีศัตรูชนะเสมอและที่ว่าไม่ดีขั้นตอนวิธีการของคุณคือกรณีที่เลวร้ายที่สุด
การแฮชเป็นอย่างไร O (1)?
สิ่งที่ทำให้เราผิดหวังในความท้าทายก่อนหน้านี้คือฝ่ายตรงข้ามรู้จักฟังก์ชันแฮชของเราเป็นอย่างดีและสามารถใช้ความรู้นั้นเพื่อสร้างข้อมูลที่แย่ที่สุดเท่าที่จะเป็นไปได้ จะเกิดอะไรขึ้นถ้าแทนที่จะใช้ฟังก์ชันแฮชคงที่เสมอเรามีชุดของฟังก์ชันแฮชH
ซึ่งอัลกอริทึมสามารถสุ่มเลือกจากรันไทม์ได้? ในกรณีที่คุณอยากรู้อยากเห็นH
เรียกว่าฟังก์ชันแฮชสากล [ 3 ] เอาล่ะเรามาลองเพิ่มการสุ่มกันดีกว่า
ก่อนอื่นสมมติว่าตารางแฮชของเรามีเมล็ดพันธุ์r
ด้วยและr
กำหนดให้เป็นตัวเลขสุ่มในเวลาก่อสร้าง เรากำหนดครั้งเดียวแล้วแก้ไขสำหรับอินสแตนซ์ตารางแฮชนั้น ตอนนี้เรามาดูรหัสเทียมของเราอีกครั้ง
function get(a: Table with m buckets and seed r, k: Key being looked up)
rHash <- H[r]
bucket <- compute rHash(k) modulo m
for each (key,value) in a[bucket]
return value if k == key
return not_found
ถ้าเราพยายามท้าทายอีกครั้งหนึ่ง: จากขั้นตอนที่ (1) ฝ่ายตรงข้ามสามารถรู้ทุกฟังก์ชันแฮชที่เรามีในแต่ตอนนี้ฟังก์ชั่นที่เฉพาะเจาะจงกัญชาเราขึ้นอยู่กับการใช้งานH
r
มูลค่าของr
มันเป็นส่วนตัวสำหรับโครงสร้างของเราฝ่ายตรงข้ามไม่สามารถตรวจสอบได้ที่รันไทม์หรือคาดการณ์ล่วงหน้าดังนั้นเขาจึงไม่สามารถสร้างรายการที่ไม่ดีสำหรับเราได้เสมอไป สมมติว่าในขั้นตอนที่ (2) ฝ่ายตรงข้ามเลือกหนึ่งฟังก์ชั่นhash
ในการH
ที่สุ่มจากนั้นเขาก็ฝีมือรายการn
ชนภายใต้hash modulo m
และส่งว่าสำหรับขั้นตอน (3) นิ้วข้ามว่าที่รันไทม์H[r]
จะเหมือนกันhash
ที่พวกเขาเลือก
นี่คือทางออกที่ร้ายแรงสำหรับปฏิปักษ์รายการที่เขาสร้างขึ้นมาภายใต้ collides hash
แต่จะเป็นเพียงการป้อนข้อมูลแบบสุ่มภายใต้กัญชาฟังก์ชันอื่น ๆ H
ใน ถ้าเขาชนะเดิมพันนี้เวลาทำงานของเราจะเป็นกรณีที่เลวร้ายO(n)
เหมือนก่อน แต่ถ้าเขาสูญเสียแล้วดีเราก็จะได้รับการป้อนข้อมูลแบบสุ่มซึ่งจะมีค่าเฉลี่ยของO(1)
เวลา และแน่นอนที่สุดครั้งที่ฝ่ายตรงข้ามจะแพ้เขาชนะเพียงครั้งเดียวในทุก|H|
ความท้าทายและเราสามารถสร้างราย|H|
ใหญ่ได้มาก
เปรียบเทียบผลลัพธ์นี้กับอัลกอริทึมก่อนหน้าซึ่งฝ่ายตรงข้ามชนะการท้าทายเสมอ การโบกมือที่นี่เล็กน้อย แต่เนื่องจากส่วนใหญ่แล้วฝ่ายตรงข้ามจะล้มเหลวและนี่เป็นเรื่องจริงสำหรับกลยุทธ์ที่เป็นไปได้ทั้งหมดที่ฝ่ายตรงข้ามสามารถลองได้ตามมาว่าแม้ว่าจะเป็นกรณีO(n)
ที่เลวร้ายที่สุด แต่ในความเป็นจริงO(1)
แล้วกรณีที่เลวร้ายที่สุดที่คาดไว้ก็คือ
อีกครั้งนี่ไม่ใช่การพิสูจน์อย่างเป็นทางการ การรับประกันที่เราได้รับจากการวิเคราะห์กรณีที่เลวร้ายที่สุดที่คาดไว้นี้คือเวลาทำงานของเราไม่ขึ้นอยู่กับข้อมูลใด นี่เป็นการรับประกันแบบสุ่มอย่างแท้จริงเมื่อเทียบกับการวิเคราะห์กรณีโดยเฉลี่ยที่เราแสดงให้เห็นว่าฝ่ายตรงข้ามที่มีแรงจูงใจสามารถสร้างปัจจัยการผลิตที่ไม่ดีได้อย่างง่ายดาย