มีการตั้งค่าอย่างไร ()


151

ฉันเคยเห็นคนพูดว่าsetวัตถุในหลามมี O (1) การตรวจสอบสมาชิก พวกเขานำมาใช้ภายในเพื่ออนุญาตสิ่งนี้ได้อย่างไร โครงสร้างข้อมูลชนิดใดที่ใช้? การใช้งานนั้นมีความหมายอะไรอีกบ้าง

ทุกคำตอบที่นี่ให้ความกระจ่างจริง ๆ แต่ฉันยอมรับได้เพียงคำตอบเดียวดังนั้นฉันจะไปกับคำตอบที่ใกล้เคียงที่สุดกับคำถามดั้งเดิมของฉัน ขอบคุณสำหรับข้อมูล!

คำตอบ:


139

ตามหัวข้อนี้ :

อันที่จริงชุดของ CPython ถูกนำไปใช้งานเหมือนพจนานุกรมที่มีค่าหุ่น (กุญแจที่เป็นสมาชิกของชุด) พร้อมกับการเพิ่มประสิทธิภาพบางอย่างที่ใช้ประโยชน์จากการขาดคุณค่านี้

ดังนั้นโดยทั่วไปแล้วsetจะใช้ hashtable เป็นโครงสร้างข้อมูลพื้นฐาน สิ่งนี้อธิบายการตรวจสอบสมาชิก O (1) เนื่องจากการค้นหารายการใน hashtable เป็นการดำเนินการ O (1) โดยเฉลี่ย

หากคุณมีความโน้มเอียงมากคุณยังสามารถเรียกดูซอร์สโค้ด CPython สำหรับชุดที่อ้างอิงจากAchim Dommaซึ่งส่วนใหญ่เป็นการตัดและวางจากdictการนำไปปฏิบัติ


18
IIRC เดิมsetการดำเนินการจริงก็ dictมีค่าหุ่นและมันได้เพิ่มประสิทธิภาพในภายหลัง
dan04

1
สถานการณ์ใหญ่ที่สุดของ O ไม่ใช่เรื่องใหญ่หรือ หากคุณสามารถหาอินสแตนซ์ที่เวลาเป็น O (n) ได้แล้วก็คือ O (n) .. ฉันไม่เข้าใจอะไรเลยในตอนนี้จากบทเรียนทั้งหมด
Claudiu Creanga

4
ไม่กรณีโดยเฉลี่ยคือ O (1) แต่กรณีที่แย่ที่สุดคือ O (N) สำหรับการค้นหาตารางแฮช
Justin Ethier

4
@ClaudiuCreanga นี้เป็นความคิดเห็นเก่า แต่เพียงเพื่อชี้แจง: สัญกรณ์บิ๊ก O บอกคุณขอบเขตบนอัตราการเติบโตของสิ่งต่าง ๆ แต่คุณสามารถผูกบนการเจริญเติบโตของประสิทธิภาพของกรณีเฉลี่ยและคุณสามารถแยกการเจริญเติบโตของกรณีที่เลวร้ายที่สุด ประสิทธิภาพ.
Kirk Boyer

79

เมื่อคนบอกว่าชุดมีการตรวจสอบสมาชิก O (1) พวกเขากำลังพูดถึงกรณีโดยเฉลี่ย ในกรณีที่เลวร้ายที่สุด (เมื่อค่าแฮชทั้งหมดชนกัน) การตรวจสอบการเป็นสมาชิกคือ O (n) ดูวิกิพีเดียหลามกับความซับซ้อนเวลา

บทความวิกิพีเดียกล่าวว่ากรณีที่ดีที่สุดO(1 + k/n)ซับซ้อนเวลาสำหรับตารางแฮชที่ไม่ได้ปรับขนาดเป็น ผลลัพธ์นี้ไม่ได้ใช้กับชุด Python โดยตรงเนื่องจากชุด Python ใช้ตารางแฮชที่ปรับขนาด

เล็ก ๆ น้อย ๆ เพิ่มเติมเกี่ยวกับบทความวิกิพีเดียกล่าวว่าสำหรับค่าเฉลี่ยกรณีและสมมติให้มีฟังก์ชั่นชุดคร่ำเครียดง่ายซับซ้อนเป็นเวลาO(1/(1-k/n))ที่สามารถกระโดดจากคงที่k/nc<1

Big-O อ้างถึงพฤติกรรมแบบอะซิมโทติคเป็น n →∞ เนื่องจาก k / n สามารถล้อมรอบด้วยค่าคงที่, c <1, เป็นอิสระจาก n ,

O(1/(1-k/n))คือไม่ใหญ่กว่าO(1/(1-c))ซึ่งเทียบเท่ากับ=O(constant)O(1)

ดังนั้นโดยเฉลี่ยแล้วการตรวจสอบสมาชิกภาพสำหรับชุด Python นั้นเป็นเรื่องO(1)ง่าย


14

ฉันคิดว่ามันเป็นความผิดพลาดทั่วไปการsetค้นหา (หรือ hashtable สำหรับเรื่องนั้น) ไม่ใช่ O (1)
จาก Wikipedia

ในรุ่นที่ง่ายที่สุดฟังก์ชันแฮชจะไม่ได้รับการระบุอย่างสมบูรณ์และตารางไม่ได้ปรับขนาด เพื่อเป็นทางเลือกที่ดีที่สุดของฟังก์ชันแฮชตารางขนาด n ที่มีการกำหนดแอดเดรสแบบเปิดไม่มีการชนและถือองค์ประกอบได้สูงสุด n รายการพร้อมการเปรียบเทียบเพียงครั้งเดียวสำหรับการค้นหาที่ประสบความสำเร็จและตารางขนาด n ที่มีการโยงและปุ่ม k มีค่าต่ำสุดสูงสุด (0, kn) การชนและการเปรียบเทียบO (1 + k / n)สำหรับการค้นหา สำหรับทางเลือกที่แย่ที่สุดของฟังก์ชั่นแฮชการแทรกทุกครั้งจะทำให้เกิดการชนกันและตารางแฮชจะลดลงเป็นการค้นหาแบบเส้นตรงโดยการเปรียบเทียบΩ (k) จะถูกตัดจำหน่ายต่อการแทรกและสูงสุดถึง k สำหรับการค้นหาที่ประสบความสำเร็จ

เกี่ยวข้อง: hashmap Java จริง ๆ O (1) หรือไม่


4
แต่พวกเขาใช้เวลาในการค้นหารายการอย่างต่อเนื่อง: python -m timeit -s "s = set (ช่วง (10))" "5 ใน s" 10,000000 ลูปที่ดีที่สุดคือ 3: 0.0642 usec ต่อวง <--> หลาม - m timeit -s "s = set (ช่วง (10,000000))" "5 ใน s" 10,000000 ลูป, ดีที่สุดคือ 3: 0.0634 usec ต่อวง ... และนั่นเป็นชุดที่ใหญ่ที่สุดที่ไม่โยน MemoryErrors
Jochen Ritzel

2
@ THC4k สิ่งที่คุณพิสูจน์ได้คือการค้นหา X เสร็จสิ้นในเวลาคงที่ แต่ไม่ได้หมายความว่าเวลาสำหรับการค้นหา X + Y จะใช้เวลาเท่ากันซึ่งเป็นสิ่งที่ O (1) เป็นเรื่องเกี่ยวกับ
Shay Erlichmen

3
@intuited: ทำได้ แต่การทดสอบด้านบนไม่ได้พิสูจน์ว่าคุณสามารถค้นหา "5" ในเวลาเดียวกันคุณสามารถค้นหา "485398" หรือหมายเลขอื่น ๆ ที่อาจอยู่ในพื้นที่ปะทะกันที่น่ากลัว มันไม่เกี่ยวกับการค้นหาองค์ประกอบเดียวกันในแฮชขนาดต่างกันในเวลาเดียวกัน (อันที่จริงไม่จำเป็นเลย) แต่มันเกี่ยวกับว่าคุณสามารถเข้าถึงแต่ละรายการในระยะเวลาเดียวกันในตารางปัจจุบันหรือไม่ - สิ่งที่เป็นไปไม่ได้โดยทั่วไปสำหรับตารางแฮชที่จะทำให้สำเร็จเนื่องจากโดยทั่วไปจะมีการชนกันอยู่เสมอ
Nick Bastin

3
กล่าวอีกนัยหนึ่งเวลาในการค้นหาขึ้นอยู่กับจำนวนของค่าที่เก็บไว้เพราะนั่นจะเพิ่มโอกาสในการชน
intuited

3
@intuited: ไม่นั่นไม่ถูกต้อง เมื่อจำนวนของค่าที่เก็บไว้เพิ่มขึ้น Python จะเพิ่มขนาดของ hashtable โดยอัตโนมัติและอัตราการชนจะคงที่โดยประมาณ สมมติว่าอัลกอริธึมการแฮ็กO (1) กระจายกันแล้วการค้นหา hashtable จะถูกตัดจำหน่าย O (1) คุณอาจต้องการดูงานนำเสนอวิดีโอ "The Mighty Dictionary" python.mirocommunity.org/video/1591/…
Lie Ryan

13

เราทุกคนสามารถเข้าถึงแหล่งข้อมูลได้อย่างง่ายดายโดยที่ความคิดเห็นก่อนหน้านี้set_lookkey()กล่าวไว้

/* set object implementation
 Written and maintained by Raymond D. Hettinger <python@rcn.com>
 Derived from Lib/sets.py and Objects/dictobject.c.
 The basic lookup function used by all operations.
 This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
 The initial probe index is computed as hash mod the table size.
 Subsequent probe indices are computed as explained in Objects/dictobject.c.
 To improve cache locality, each probe inspects a series of consecutive
 nearby entries before moving on to probes elsewhere in memory.  This leaves
 us with a hybrid of linear probing and open addressing.  The linear probing
 reduces the cost of hash collisions because consecutive memory accesses
 tend to be much cheaper than scattered probes.  After LINEAR_PROBES steps,
 we then use open addressing with the upper bits from the hash value.  This
 helps break-up long chains of collisions.
 All arithmetic on hash should ignore overflow.
 Unlike the dictionary implementation, the lookkey function can return
 NULL if the rich comparison returns an error.
*/


...
#ifndef LINEAR_PROBES
#define LINEAR_PROBES 9
#endif

/* This must be >= 1 */
#define PERTURB_SHIFT 5

static setentry *
set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)  
{
...

2
คำตอบนี้จะได้รับประโยชน์จาก C เน้นไวยากรณ์ Python เน้นไวยากรณ์ของความคิดเห็นที่ดูไม่ดีจริงๆ
user202729

เกี่ยวกับความคิดเห็น "สิ่งนี้ทำให้เรามีไฮบริดของการตรวจสอบเชิงเส้นและการเปิดที่อยู่" ไม่ใช่การตรวจสอบความละเอียดเชิงเส้นในการระบุที่อยู่แบบเปิดตามที่อธิบายไว้ในen.wikipedia.org/wiki/Open_addressing ? ดังนั้นการตรวจสอบเชิงเส้นจึงเป็นประเภทย่อยของที่อยู่แบบเปิดและความคิดเห็นไม่สมเหตุสมผล
Alan Evangelista

2

เพื่อเน้นความแตกต่างเล็กน้อยระหว่างset'sและต่อdict'sไปนี้เป็นข้อความที่ตัดตอนมาจากsetobject.cส่วนความคิดเห็นซึ่งชี้แจงความแตกต่างที่สำคัญของชุดต่อเทียบกับ dicts

ใช้กรณีสำหรับชุดที่แตกต่างกันอย่างมากจากพจนานุกรมที่คีย์ค้นหามีแนวโน้มที่จะมีอยู่ ในทางตรงกันข้ามชุดการทดสอบส่วนใหญ่เกี่ยวกับการเป็นสมาชิกโดยที่ไม่ทราบล่วงหน้าว่ามีองค์ประกอบใดบ้าง ดังนั้นชุดการนำไปปฏิบัติจำเป็นต้องปรับให้เหมาะสมสำหรับทั้งกรณีที่พบและไม่พบ

แหล่งที่มาของGitHub

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.