ฉันต้องการคำจำกัดความที่เป็นทางการน้อยที่สุดเท่าที่จะทำได้
ฉันต้องการคำจำกัดความที่เป็นทางการน้อยที่สุดเท่าที่จะทำได้
คำตอบ:
หมายเหตุสั้น ๆ นี่เป็นสัญญลักษณ์ที่สับสนอย่างยิ่งของ Big O (ซึ่งเป็นขอบเขตบน) กับสัญกรณ์ Theta "Θ" (ซึ่งเป็นขอบเขตสองด้าน) จากประสบการณ์ของฉันนี่เป็นเรื่องปกติของการอภิปรายในการตั้งค่าที่ไม่ใช่เชิงวิชาการ ขออภัยในความสับสนที่เกิดขึ้น
ความซับซ้อนของ Big O สามารถมองเห็นได้ด้วยกราฟนี้:
คำจำกัดความที่ง่ายที่สุดที่ฉันสามารถให้สำหรับสัญกรณ์ Big-O คือ:
สัญลักษณ์ Big-O เป็นการแสดงถึงความซับซ้อนของอัลกอริทึม
มีคำที่สำคัญและเลือกอย่างรอบคอบในประโยคนั้น:
- สัมพัทธ์:คุณสามารถเปรียบเทียบแอปเปิ้ลกับแอปเปิ้ลเท่านั้น คุณไม่สามารถเปรียบเทียบอัลกอริทึมเพื่อทำการคูณเลขคณิตกับอัลกอริทึมที่เรียงลำดับรายการจำนวนเต็ม แต่การเปรียบเทียบอัลกอริธึมสองอย่างเพื่อทำการคำนวณทางคณิตศาสตร์ (การคูณหนึ่งการบวกหนึ่ง) จะบอกบางสิ่งที่มีความหมายกับคุณ
- การแทนค่า: Big-O (ในรูปแบบที่ง่ายที่สุด) ช่วยลดการเปรียบเทียบระหว่างอัลกอริธึมกับตัวแปรเดี่ยว ตัวแปรนั้นถูกเลือกขึ้นอยู่กับการสังเกตหรือการสันนิษฐาน ตัวอย่างเช่นอัลกอริทึมการเรียงลำดับจะถูกเปรียบเทียบโดยยึดตามการดำเนินการเปรียบเทียบ (การเปรียบเทียบสองโหนดเพื่อพิจารณาลำดับการจัดเรียง) สมมติว่าการเปรียบเทียบมีราคาแพง แต่ถ้าการเปรียบเทียบราคาถูก แต่การแลกเปลี่ยนมีราคาแพง มันเปลี่ยนการเปรียบเทียบ และ
- ความซับซ้อน:ถ้าฉันใช้เวลาหนึ่งวินาทีในการเรียงลำดับ 10,000 องค์ประกอบมันจะใช้เวลานานแค่ไหนในการเรียงลำดับหนึ่งล้านชิ้น? ความซับซ้อนในอินสแตนซ์นี้เป็นการวัดที่สัมพันธ์กับสิ่งอื่น
กลับมาอ่านข้อมูลข้างต้นเมื่อคุณอ่านส่วนที่เหลือ
ตัวอย่างที่ดีที่สุดของ Big-O ที่ฉันนึกได้คือทำเลขคณิต ใช้ตัวเลขสองตัว (123456 และ 789012) การดำเนินการทางคณิตศาสตร์ขั้นพื้นฐานที่เราเรียนรู้ในโรงเรียนคือ:
- ส่วนที่เพิ่มเข้าไป;
- การลบ;
- คูณ; และ
- แผนก.
แต่ละสิ่งเหล่านี้เป็นการดำเนินการหรือปัญหา วิธีการในการแก้ปัญหาเหล่านี้จะเรียกว่าอัลกอริทึม
นอกจากนี้ที่ง่ายที่สุดคือ คุณจัดเรียงหมายเลขขึ้น (ทางขวา) และเพิ่มตัวเลขลงในคอลัมน์ที่เขียนหมายเลขล่าสุดของการเพิ่มนั้นในผลลัพธ์ ส่วน 'หมื่น' ของตัวเลขนั้นถูกพาไปยังคอลัมน์ถัดไป
สมมติว่าการเพิ่มตัวเลขเหล่านี้เป็นการดำเนินการที่แพงที่สุดในอัลกอริทึมนี้ เหตุผลที่การเพิ่มตัวเลขสองตัวนี้เข้าด้วยกันเราต้องบวกกัน 6 หลัก (และอาจมีเลข 7) ถ้าเราบวกสองหลัก 100 หลักเข้าด้วยกันเราจะต้องเพิ่ม 100 ครั้ง หากเราเพิ่มตัวเลขสองหลัก 10,000 เราต้องเพิ่ม 10,000 ครั้ง
เห็นรูปแบบ? ซับซ้อน (เป็นจำนวนของการดำเนินงาน) เป็นสัดส่วนโดยตรงกับจำนวนตัวเลขnในจำนวนที่มีขนาดใหญ่ เราเรียกสิ่งนี้O (n)หรือความซับซ้อนเชิงเส้น
การลบคล้ายกัน (ยกเว้นคุณอาจต้องยืมแทนการพกพา)
การคูณแตกต่างกัน คุณเรียงตัวเลขให้มากขึ้นนำตัวเลขตัวแรกในเลขท้ายแล้วคูณด้วยตัวเลขแต่ละตัวในจำนวนสูงสุดและผ่านแต่ละหลัก ดังนั้นในการคูณตัวเลข 6 หลักสองตัวของเราเราต้องทำ 36 คูณ เราอาจต้องเพิ่มคอลัมน์มากถึง 10 หรือ 11 คอลัมน์เพื่อให้ได้ผลลัพธ์ที่ต้องการ
หากเรามีตัวเลขสองหลัก 100 หลักเราต้องคูณ 10,000 และเพิ่ม 200 สำหรับตัวเลขสองล้านตัวเราต้องทำการคูณหนึ่งล้านล้าน (10 12 ) และเพิ่มอีกสองล้าน
ในฐานะที่เป็นเกล็ดอัลกอริทึมที่มีการละลายของสแควร์นี้เป็นO (n 2 )หรือความซับซ้อนกำลังสอง นี่เป็นเวลาที่ดีที่จะแนะนำแนวคิดที่สำคัญอีกประการหนึ่ง:
เราใส่ใจเฉพาะส่วนที่ซับซ้อนที่สุดเท่านั้น
คนฉลาดอาจรู้ว่าเราสามารถแสดงจำนวนการดำเนินการเป็น: n 2 + 2n แต่อย่างที่คุณเห็นจากตัวอย่างของเราที่มีตัวเลขสองล้านตัวต่อปีเทอมที่สอง (2n) ไม่มีนัยสำคัญ (คิดเป็น 0.0002% ของการดำเนินงานทั้งหมดในระยะนั้น)
หนึ่งสามารถสังเกตเห็นว่าเราได้สมมติสถานการณ์กรณีที่เลวร้ายที่สุดที่นี่ ในขณะที่คูณตัวเลข 6 หลักหากหนึ่งในนั้นมี 4 หลักและอีกหนึ่งมี 6 หลักแล้วเรามี 24 คูณเท่านั้น ถึงกระนั้นเราคำนวณสถานการณ์กรณีที่เลวร้ายที่สุดสำหรับ 'n' นั่นคือเมื่อทั้งสองเป็นตัวเลข 6 หลัก ดังนั้น Big-O สัญกรณ์เป็นเรื่องเกี่ยวกับสถานการณ์ที่เลวร้ายที่สุดของอัลกอริทึม
ตัวอย่างที่ดีที่สุดถัดไปที่ฉันนึกได้คือสมุดโทรศัพท์โดยทั่วไปเรียกว่า White Pages หรือคล้ายกัน แต่แตกต่างกันไปในแต่ละประเทศ แต่ฉันกำลังพูดถึงคนที่แสดงรายชื่อผู้คนด้วยนามสกุลแล้วชื่อย่อหรือชื่ออาจเป็นที่อยู่แล้วหมายเลขโทรศัพท์
ตอนนี้ถ้าคุณกำลังสั่งให้คอมพิวเตอร์ค้นหาหมายเลขโทรศัพท์สำหรับ "John Smith" ในสมุดโทรศัพท์ที่มีชื่อ 1,000,000 ชื่อคุณจะทำอย่างไร ไม่สนใจข้อเท็จจริงที่ว่าคุณสามารถเดาได้ว่าในการเริ่มต้น S นั้นไกลแค่ไหน (สมมติว่าคุณทำไม่ได้) คุณจะทำอย่างไร
การดำเนินทั่วไปอาจจะเปิดขึ้นไปตรงกลางใช้เวลา 500,000 ปีบริบูรณ์และเปรียบเทียบกับ "สมิ ธ" ถ้ามันเป็น "Smith, John" เราแค่โชคดีจริงๆ มีโอกาสมากขึ้นคือ "John Smith" จะอยู่ก่อนหรือหลังชื่อ หากหลังจากเราแบ่งครึ่งสมุดโทรศัพท์ครึ่งหนึ่งแล้วทำซ้ำ หากก่อนหน้านี้เราแบ่งครึ่งแรกของสมุดโทรศัพท์ออกเป็นสองส่วนและทำซ้ำ และอื่น ๆ
สิ่งนี้เรียกว่าการค้นหาแบบไบนารี่และใช้ในการเขียนโปรแกรมทุกวันไม่ว่าคุณจะรู้หรือไม่ก็ตาม
ดังนั้นหากคุณต้องการค้นหาชื่อในสมุดโทรศัพท์ชื่อกว่าล้านชื่อคุณสามารถค้นหาชื่อใด ๆ โดยทำอย่างนี้มากที่สุด 20 ครั้ง ในการเปรียบเทียบอัลกอริธึมการค้นหาเราตัดสินใจว่าการเปรียบเทียบนี้คือ 'n' ของเรา
- สำหรับสมุดโทรศัพท์ 3 ชื่อจะทำการเปรียบเทียบ 2 ครั้ง (มากที่สุด)
- สำหรับ 7 จะใช้เวลาไม่เกิน 3
- สำหรับ 15 ใช้เวลา 4
- ...
- สำหรับ 1,000,000 ใช้เวลา 20
มันดีใช่มั้ยล่ะ?
ในแง่ Big-O นี้คือO (log n)หรือซับซ้อนลอการิทึม ตอนนี้ลอการิทึมที่เป็นปัญหาอาจเป็น ln (ฐาน e), บันทึก10 , บันทึก2หรือฐานอื่น ๆ ไม่สำคัญว่ามันจะยังคงเป็น O (log n) เช่นเดียวกับ O (2n 2 ) และ O (100n 2 ) ยังคงเป็นทั้ง O (n 2 )
เป็นจุดที่คุ้มค่าที่จะอธิบายว่า Big O สามารถใช้เพื่อกำหนดสามกรณีด้วยอัลกอริทึม:
- กรณีที่ดีที่สุด:ในการค้นหาสมุดโทรศัพท์กรณีที่ดีที่สุดคือเราพบชื่อในการเปรียบเทียบเดียว นี่คือO (1)หรือความซับซ้อนคงที่ ;
- กรณีที่คาดหวัง:ตามที่กล่าวไว้ข้างต้นนี่คือ O (บันทึก n); และ
- กรณีที่แย่ที่สุด:นี่คือ O (บันทึก n)
โดยปกติเราไม่สนใจกรณีที่ดีที่สุด เราสนใจในกรณีที่คาดหวังและเลวร้ายที่สุด บางครั้งสิ่งใดสิ่งหนึ่งเหล่านี้จะมีความสำคัญมากกว่า
กลับไปที่สมุดโทรศัพท์
ถ้าคุณมีหมายเลขโทรศัพท์และต้องการค้นหาชื่อ ตำรวจมีสมุดโทรศัพท์แบบย้อนกลับ แต่การค้นหาเช่นนี้จะถูกปฏิเสธต่อสาธารณชนทั่วไป หรือว่าพวกเขา? ในทางเทคนิคคุณสามารถค้นหาเบอร์ในสมุดโทรศัพท์ธรรมดาได้ อย่างไร?
คุณเริ่มต้นที่ชื่อและเปรียบเทียบจำนวน ถ้ามันเป็นการแข่งขันที่ยอดเยี่ยมถ้าไม่คุณจะไปต่อ คุณต้องทำเช่นนี้เพราะสมุดโทรศัพท์ไม่มีการเรียงลำดับ (ตามหมายเลขโทรศัพท์อยู่แล้ว)
ดังนั้นเพื่อค้นหาชื่อที่ระบุหมายเลขโทรศัพท์ (ค้นหาแบบย้อนกลับ):
- กรณีที่ดีที่สุด: O (1);
- กรณีที่คาดหวัง: O (n) (สำหรับ 500,000); และ
- กรณีที่เลวร้ายที่สุด: O (n) (สำหรับ 1,000,000)
นี่เป็นปัญหาที่มีชื่อเสียงในด้านวิทยาศาสตร์คอมพิวเตอร์และสมควรได้รับการกล่าวถึง ในปัญหานี้คุณมีเมือง N แห่ง แต่ละเมืองเหล่านั้นเชื่อมโยงกับเมืองอื่น ๆ อย่างน้อย 1 แห่งโดยถนนที่มีระยะทางไกล ปัญหาพนักงานขายนักท่องเที่ยวคือการหาทัวร์ที่สั้นที่สุดที่เข้าชมทุกเมือง
ฟังดูง่าย? คิดอีกครั้ง.
หากคุณมี 3 เมือง A, B และ C ที่มีถนนระหว่างทุกคู่คุณสามารถไปที่:
- A → B → C
- A → C → B
- B → C → A
- B → A → C
- C → A → B
- C → B → A
ที่จริงมีน้อยกว่านั้นเพราะบางส่วนของสิ่งเหล่านี้มีค่าเท่ากัน (A → B → C และ C → B → A จะเทียบเท่าเช่นเพราะพวกเขาใช้ถนนสายเดียวกัน
ในความเป็นจริงมี 3 ความเป็นไปได้
- นำสิ่งนี้ไปยัง 4 เมืองและคุณมีความเป็นไปได้ (iirc) 12
- ด้วย 5 มันคือ 60
- 6 กลายเป็น 360
นี่คือการทำงานของการดำเนินการทางคณิตศาสตร์ที่เรียกว่าปัจจัย โดยทั่วไป:
- 5! = 5 × 4 × 3 × 2 × 1 = 120
- 6! = 6 × 5 × 4 × 3 × 2 × 1 = 720
- 7! = 7 × 6 × 5 × 4 × 3 × 2 × 1 = 5040
- ...
- 25! = 25 × 24 ×…× 2 × 1 = 15,511,210,043,330,985,984,000,000
- ...
- 50! = 50 × 49 ×…× 2 × 1 = 3.04140932 × 10 64
ดังนั้น Big-O ของปัญหาพนักงานขายที่เดินทางเป็นO (n)หรือความซับซ้อนหรือปัจจัยรวมกัน
ตามเวลาที่คุณไปถึง 200 เมืองไม่มีเวลาเหลือพอในจักรวาลเพื่อแก้ปัญหาด้วยคอมพิวเตอร์แบบดั้งเดิม
สิ่งที่ต้องคิด
อีกจุดหนึ่งที่ผมอยากจะให้กล่าวถึงอย่างรวดเร็วก็คือขั้นตอนวิธีการใด ๆ ที่มีความซับซ้อนของO (n )จะกล่าวว่ามีความซับซ้อนพหุนามหรือแก้ปัญหาได้ในเวลาพหุนาม
O (n), O (n 2 ) เป็นต้นเป็นเวลาพหุนามทั้งหมด ปัญหาบางอย่างไม่สามารถแก้ไขได้ในเวลาพหุนาม บางสิ่งถูกใช้ในโลกด้วยเหตุนี้ การเข้ารหัสคีย์สาธารณะเป็นตัวอย่างสำคัญ เป็นการยากที่จะคำนวณหาปัจจัยสำคัญสองอย่างที่มีจำนวนมาก หากไม่ใช่เราไม่สามารถใช้ระบบกุญแจสาธารณะที่เราใช้
อย่างไรก็ตามนั่นเป็นเพียงคำอธิบายของฉัน (หวังว่าจะเป็นภาษาอังกฤษ) สำหรับ Big O (แก้ไข)
มันแสดงให้เห็นว่าอัลกอริทึมปรับขนาดตามขนาดอินพุต
O (n 2 ) : รู้จักกันในชื่อความซับซ้อนกำลังสอง
ขอให้สังเกตว่าจำนวนรายการที่เพิ่มขึ้นโดยปัจจัยที่ 10 แต่การเพิ่มขึ้นของเวลาโดยปัจจัยที่ 10 2 โดยทั่วไป n = 10 และอื่น ๆ O (n 2 ) ทำให้เรามีปัจจัยปรับ n 2ซึ่งเป็น 10 2
O (n) : รู้จักกันในชื่อLinear complex
เวลานี้จำนวนไอเท็มเพิ่มขึ้น 10 เท่าและเวลาก็เพิ่มขึ้น n = 10 และปัจจัยการปรับสเกลของ O (n) คือ 10
O (1) : เรียกว่าความซับซ้อนคงที่
จำนวนรายการยังคงเพิ่มขึ้น 10 เท่า แต่สัดส่วนการปรับของ O (1) เท่ากับ 1 เสมอ
O (บันทึก n) : รู้จักกันในชื่อความซับซ้อนลอการิทึม
จำนวนการคำนวณจะเพิ่มขึ้นโดยบันทึกของค่าอินพุตเท่านั้น ดังนั้นในกรณีนี้สมมติว่าแต่ละคำนวณจะใช้เวลา 1 วินาทีเข้าสู่ระบบของการป้อนข้อมูลเป็นเวลาที่จำเป็นจึงn
log n
นั่นคือส่วนสำคัญของมัน พวกมันลดจำนวนคณิตศาสตร์ลงดังนั้นมันอาจจะไม่ใช่ n 2หรืออะไรก็ตามที่พวกเขาพูด แต่มันจะเป็นปัจจัยสำคัญในการปรับขนาด
Big-O สัญกรณ์ (ที่เรียกว่า "การเจริญเติบโต asymptotic" สัญกรณ์) เป็นสิ่งที่ฟังก์ชั่น "มีลักษณะเหมือน" เมื่อคุณไม่สนใจปัจจัยคงที่และสิ่งที่อยู่ใกล้กับแหล่งกำเนิด เราใช้มันเพื่อพูดคุยเกี่ยวกับวิธีการสิ่งที่ขนาด
ข้อมูลพื้นฐานเกี่ยวกับ
สำหรับอินพุตขนาดใหญ่ "เพียงพอ" ...
f(x) ∈ O(upperbound)
หมายถึงf
"เติบโตไม่เร็วกว่า"upperbound
f(x) ∈ Ɵ(justlikethis)
หมายถึงf
"เติบโตเหมือน"justlikethis
f(x) ∈ Ω(lowerbound)
หมายถึงf
"เติบโตไม่ช้ากว่า"lowerbound
ใหญ่-O โน้ตไม่สนใจเกี่ยวกับปัจจัยคงที่: ฟังก์ชั่น9x²
บอกว่าจะ 10x²
"เติบโตเหมือนกัน" ไม่ไม่ใหญ่ O- เชิงสัญกรณ์เกี่ยวกับการดูแลที่ไม่ asymptoticสิ่ง ( "สิ่งที่อยู่ใกล้กับแหล่งกำเนิด" หรือ "สิ่งที่เกิดขึ้นเมื่อขนาดของปัญหาที่มีขนาดเล็ก"): ฟังก์ชั่น10x²
บอกว่าจะ 10x² - x + 2
"เติบโตเหมือนกัน"
ทำไมคุณถึงต้องการละเว้นส่วนเล็ก ๆ ของสมการ? เพราะพวกมันกลายเป็นแคระอย่างสมบูรณ์โดยส่วนใหญ่ของสมการเมื่อคุณพิจารณาสเกลที่ใหญ่ขึ้นและใหญ่ขึ้น ผลงานของพวกเขากลายเป็นแคระและไม่เกี่ยวข้อง (ดูหัวข้อตัวอย่าง)
อีกวิธีคือทั้งหมดเกี่ยวกับอัตราส่วนเมื่อคุณไปหาอนันต์ หากคุณแบ่งเวลาจริงตามที่ใช้O(...)
คุณจะได้รับปัจจัยคงที่ในการ จำกัด อินพุตที่มีขนาดใหญ่ สิ่งนี้เหมาะสม: ฟังก์ชั่น "สเกลเหมือนกัน" ถ้าคุณสามารถคูณให้ได้อีกตัวหนึ่ง นั่นคือเมื่อเราพูดว่า ...
actualAlgorithmTime(N) ∈ O(bound(N))
e.g. "time to mergesort N elements
is O(N log(N))"
... นี่หมายความว่าสำหรับปัญหา "ใหญ่พอ" ขนาด N (ถ้าเราไม่สนใจสิ่งที่อยู่ใกล้แหล่งกำเนิด) มีค่าคงที่บางอย่าง (เช่น 2.5 ทำขึ้นทั้งหมด) เช่นนั้น:
actualAlgorithmTime(N) e.g. "mergesort_duration(N) "
────────────────────── < constant ───────────────────── < 2.5
bound(N) N log(N)
มีหลายทางเลือกของค่าคงที่; บ่อยครั้งที่ตัวเลือก "ดีที่สุด" เรียกว่า "ปัจจัยคงที่" ของอัลกอริทึม ... แต่เรามักจะเพิกเฉยเพราะเราเพิกเฉยต่อคำที่ไม่ใหญ่ที่สุด (ดูหัวข้อปัจจัยคงที่ว่าทำไมพวกเขาไม่สำคัญ) คุณสามารถนึกถึงสมการข้างต้นว่าเป็นขอบเขตโดยพูดว่า " ในกรณีที่เลวร้ายที่สุดเวลาที่ใช้จะไม่เลวร้ายกว่าคร่าวๆN*log(N)
ภายในปัจจัย 2.5 (ปัจจัยคงที่เราไม่สนใจมาก) " .
โดยทั่วไปแล้วO(...)
เป็นสิ่งที่มีประโยชน์มากที่สุดเพราะเรามักจะสนใจพฤติกรรมที่เลวร้ายที่สุด หากf(x)
แสดงถึงบางสิ่งที่ "ไม่ดี" เช่นการใช้หน่วยประมวลผลหรือหน่วยความจำ " f(x) ∈ O(upperbound)
" หมายถึง " upperbound
เป็นกรณีที่เลวร้ายที่สุดของการใช้งานตัวประมวลผล / หน่วยความจำ"
การประยุกต์ใช้งาน
ในฐานะที่เป็นโครงสร้างทางคณิตศาสตร์ล้วนๆสัญกรณ์ใหญ่ -O ไม่ได้ จำกัด อยู่ที่การพูดคุยเกี่ยวกับเวลาในการประมวลผลและหน่วยความจำ คุณสามารถใช้มันเพื่อหารือเกี่ยวกับ asymptotics ของสิ่งใดก็ตามที่การปรับสเกลมีความหมายเช่น:
N
ผู้คนในงานปาร์ตี้ ( Ɵ(N²)
เฉพาะเจาะจงN(N-1)/2
แต่สิ่งที่สำคัญคือ "เกล็ดเหมือน" N²
)ตัวอย่าง
สำหรับตัวอย่างการจับมือกันดังกล่าวข้างต้นทุกคนอยู่ในมือทุกคนห้องพักสั่นอื่น ในตัวอย่าง#handshakes ∈ Ɵ(N²)
นั้น ทำไม?
สำรองข้อมูลเล็กน้อย: จำนวนของการจับมือเป็นสิ่งที่ n-select-2 หรือN*(N-1)/2
(คน N คนแต่ละคนจับมือของคนอื่น ๆ N-1 แต่การจับมือสองครั้งนี้นับด้วย 2):
อย่างไรก็ตามสำหรับคนจำนวนมากเทอมเชิงเส้นN
จะถูกแคระและก่อให้เกิด 0 อย่างมีประสิทธิภาพต่ออัตราส่วน (ในแผนภูมิ: เศษส่วนของกล่องว่างบนเส้นทแยงมุมบนกล่องทั้งหมดจะเล็กลงเมื่อจำนวนผู้เข้าร่วมใหญ่ขึ้น) ดังนั้นพฤติกรรมการปรับสเกลคือorder N²
หรือจำนวนของการจับมือ "เติบโตเหมือนN²"
#handshakes(N)
────────────── ≈ 1/2
N²
มันเหมือนกับว่ากล่องเปล่าในแนวทแยงของแผนภูมิ (N * (N-1) / 2 เครื่องหมายถูก) ไม่ได้อยู่ที่นั่น (N 2เครื่องหมายถูกแบบไม่มีสัญญาณ)
(การพูดนอกเรื่องชั่วคราวจาก "plain English" :) หากคุณต้องการพิสูจน์สิ่งนี้กับตัวคุณเองคุณสามารถทำพีชคณิตแบบง่าย ๆ ในอัตราส่วนเพื่อแยกมันออกเป็นหลาย ๆ คำศัพท์ ( lim
หมายถึง "ถือว่าอยู่ในขีด จำกัด " เพียงแค่เพิกเฉยถ้าคุณ ไม่ได้เห็นมันเป็นเพียงสัญกรณ์สำหรับ "และ N ใหญ่จริงๆ"):
N²/2 - N/2 (N²)/2 N/2 1/2
lim ────────── = lim ( ────── - ─── ) = lim ─── = 1/2
N→∞ N² N→∞ N² N² N→∞ 1
┕━━━┙
this is 0 in the limit of N→∞:
graph it, or plug in a really large number for N
tl; dr: จำนวนของ handshakes 'ดูเหมือนว่า' x²มากสำหรับค่าขนาดใหญ่ว่าถ้าเราต้องเขียนอัตราส่วน # handshakes / x²ความจริงที่ว่าเราไม่ต้องการการจับมือx² แน่นอนจะไม่ปรากฏขึ้น ในทศนิยมสำหรับขนาดใหญ่โดยพลการในขณะที่
เช่นสำหรับ x = 1million, อัตราส่วน # handshakes / x²: 0.499999 ...
อาคารปรีชา
สิ่งนี้ทำให้เราสร้างประโยคเช่น ...
"สำหรับขนาดใหญ่พอ = n ไม่ว่าปัจจัยคงที่คืออะไรถ้าฉันเป็นสองเท่าขนาดอินพุตเป็น ...
N → (2N) = 2 ( N )
N² → (2N) ² = 4 ( N² )
cN³ → c (2N) ³ = 8 ( cN³ )
c log (N) → c log (2N) = (c log (2)) + ( c log (N) ) = (จำนวนเงินคงที่) + ( c log (N) )
c * 1 → c * 1
มันน้อยกว่า O (N 1.000001 ) ซึ่งคุณอาจยินดีที่จะโทรเป็นเส้นตรง
2 N → 2 2N = (4 N ) ............ ใส่วิธีอื่น ...... 2 N → 2 N + 1 = 2 N 2 1 = 2 2 N
[สำหรับความโน้มเอียงทางคณิตศาสตร์คุณสามารถเลื่อนเมาส์ไปที่สปอยเลอร์สำหรับไซเดอร์น้อย]
(ด้วยเครดิตไปที่https://stackoverflow.com/a/487292/711085 )
(ในทางเทคนิคปัจจัยคงที่อาจมีความสำคัญในตัวอย่างที่ลึกลับบางอย่าง แต่ฉันได้ใช้ถ้อยคำข้างต้น (เช่นใน log (N)) โดยที่มันไม่ได้)
นี่เป็นคำสั่งการเติบโตของขนมปังและเนยที่โปรแกรมเมอร์และนักวิทยาศาสตร์คอมพิวเตอร์ใช้เป็นจุดอ้างอิง พวกเขาเห็นสิ่งเหล่านี้ตลอดเวลา (ดังนั้นในขณะที่คุณสามารถคิดทางเทคนิค "การป้อนข้อมูลเป็นสองเท่าทำให้อัลกอริทึม O (√N) ช้าลง 1.414 เท่า" ควรคิดให้ดีกว่าเพราะ "นี่แย่กว่าลอการิทึม แต่ดีกว่าเชิงเส้น")
ปัจจัยคงที่
โดยปกติเราไม่สนใจว่าปัจจัยคงที่ที่เฉพาะเจาะจงคืออะไรเพราะพวกเขาไม่ได้ส่งผลกระทบต่อการเติบโตของฟังก์ชัน ตัวอย่างเช่นอัลกอริธึมทั้งสองอาจใช้O(N)
เวลาในการทำให้เสร็จ แต่อาจจะช้ากว่าสองเท่า เรามักจะไม่สนใจมากเกินไปเว้นแต่ปัจจัยที่มีขนาดใหญ่มากตั้งแต่การเพิ่มประสิทธิภาพเป็นธุรกิจที่หากิน ( เมื่อก่อนวัยอันควรคือการเพิ่มประสิทธิภาพ? ) เช่นเดียวกับการเลือกอัลกอริทึมที่มี big-O ที่ดีกว่ามักจะปรับปรุงประสิทธิภาพตามคำสั่งของขนาด
บางขั้นตอนวิธีการที่เหนือกว่า asymptotically (เช่นไม่ใช่การเปรียบเทียบO(N log(log(N)))
การจัดเรียง) สามารถมีปัจจัยคงที่ขนาดใหญ่ (เช่น100000*N log(log(N))
) หรือค่าใช้จ่ายที่ค่อนข้างขนาดใหญ่เช่นO(N log(log(N)))
กับที่ซ่อนอยู่+ 100*N
ที่พวกเขาไม่ค่อยจะมีมูลค่าการใช้แม้ใน "ข้อมูลขนาดใหญ่"
เหตุใด O (N) จึงดีที่สุดที่คุณสามารถทำได้นั่นคือสาเหตุที่เราต้องการโครงสร้างข้อมูล
O(N)
อัลกอริทึมอยู่ในความรู้สึก "ดีที่สุด" อัลกอริทึมถ้าคุณต้องการอ่านข้อมูลทั้งหมดของคุณ การกระทำของการอ่านกลุ่มข้อมูลเป็นการO(N)
ดำเนินการ โดยทั่วไปแล้วการโหลดลงในหน่วยความจำO(N)
(หรือเร็วกว่าถ้าคุณมีฮาร์ดแวร์รองรับหรือไม่มีเวลาเลยถ้าคุณอ่านข้อมูลไปแล้ว) อย่างไรก็ตามหากคุณสัมผัสหรือดูข้อมูลทุกส่วน (หรือแม้แต่ข้อมูลอื่น ๆ ) อัลกอริทึมของคุณจะใช้O(N)
เวลาในการค้นหา ไม่ว่าอัลกอริทึมที่แท้จริงของคุณจะใช้เวลานานเท่าไหร่มันก็จะเป็นอย่างน้อยO(N)
เพราะมันใช้เวลาในการดูข้อมูลทั้งหมด
เดียวกันสามารถกล่าวว่าสำหรับการกระทำมากของการเขียน ขั้นตอนวิธีการทั้งหมดที่พิมพ์ออกจากสิ่งที่ไม่มีจะใช้เวลา N เพราะการส่งออกเป็นอย่างน้อยที่ยาว (เช่นพิมพ์ออกพีชคณิตทั้งหมด (วิธีการจัดเรียง) ชุดของ N เล่นไพ่เป็นปัจจัย: O(N!)
)
สิ่งนี้กระตุ้นให้มีการใช้โครงสร้างข้อมูล : โครงสร้างข้อมูลจำเป็นต้องอ่านข้อมูลเพียงครั้งเดียว (โดยปกติคือO(N)
เวลา) บวกกับการประมวลผลล่วงหน้าจำนวนหนึ่ง (เช่นO(N)
หรือO(N log(N))
หรือO(N²)
) ที่เราพยายามทำให้มีขนาดเล็ก หลังจากนั้นการปรับเปลี่ยนโครงสร้างข้อมูล (แทรก / ลบ / ฯลฯ ) และทำการสืบค้นข้อมูลที่ใช้เวลาน้อยมากเช่นหรือO(1)
O(log(N))
จากนั้นคุณทำการค้นหาจำนวนมาก! โดยทั่วไปยิ่งคุณเต็มใจทำงานเร็วขึ้นเท่าไหร่งานที่คุณต้องทำในภายหลังก็จะยิ่งน้อยลง
ตัวอย่างเช่นสมมติว่าคุณมีพิกัดละติจูดและลองจิจูดของส่วนถนนนับล้านและต้องการหาทางแยกถนนทั้งหมด
O(N)
เพียงครั้งเดียว แต่ถ้าคุณต้องการทำหลาย ๆ ครั้ง (ในกรณีนี้N
ครั้งหนึ่งครั้งสำหรับแต่ละส่วน) จำเป็นต้องO(N²)
ทำงานหรือ1000000² = การดำเนินการ 1000000000000 ไม่ดี (คอมพิวเตอร์สมัยใหม่สามารถทำงานได้หนึ่งพันล้านครั้งต่อวินาที)O(N)
ตามเวลาที่กำหนดไว้ล่วงหน้า หลังจากนั้นก็ใช้เวลาโดยเฉลี่ยในการค้นหาบางสิ่งด้วยคีย์ของมันเท่านั้น (ในกรณีนี้คีย์ของเราคือพิกัดละติจูดและลองจิจูดปัดเศษเป็นกริดเราค้นหากริดสเปซที่อยู่ติดกันซึ่งมีเพียง 9 ซึ่งเป็น คงที่)O(N²)
สามารถจัดการได้O(N)
และสิ่งที่เราต้องทำก็คือจ่ายค่าใช้จ่ายเล็กน้อยเพื่อทำตารางแฮชคุณธรรมของเรื่องราว: โครงสร้างข้อมูลช่วยให้เราเร่งดำเนินการได้เร็วขึ้น ยิ่งไปกว่านั้นโครงสร้างข้อมูลขั้นสูงยังช่วยให้คุณสามารถรวมล่าช้าหรือเพิกเฉยต่อการทำงานด้วยวิธีที่ชาญฉลาดอย่างเหลือเชื่อ ปัญหาที่แตกต่างกันจะมีการเปรียบเทียบที่แตกต่างกัน แต่พวกเขาทั้งหมดเกี่ยวข้องกับการจัดระเบียบข้อมูลในลักษณะที่ใช้ประโยชน์จากโครงสร้างบางอย่างที่เราสนใจหรือที่เราได้กำหนดไว้เพื่อทำบัญชี เราทำงานล่วงหน้า (โดยทั่วไปการวางแผนและการจัดระเบียบ) และตอนนี้งานที่ทำซ้ำได้ง่ายขึ้นมาก!
ตัวอย่างที่ใช้งานได้จริง: แสดงคำสั่งของการเติบโตในขณะที่เขียนโค้ด
สัญกรณ์เชิงซีโมติกนั้นอยู่ที่ใจกลางของมันค่อนข้างแยกจากการเขียนโปรแกรม สัญลักษณ์แบบอะซิมโทติคเป็นกรอบทางคณิตศาสตร์สำหรับการคิดเกี่ยวกับขนาดของสิ่งต่าง ๆ และสามารถใช้ในด้านต่าง ๆ ได้ ที่กล่าวว่า ... นี่คือวิธีการที่คุณใช้เครื่องหมายกำกับเชิงสัญลักษณ์กับการเข้ารหัส
พื้นฐาน: เมื่อใดก็ตามที่เราโต้ตอบกับทุกองค์ประกอบในคอลเลกชันขนาด A (เช่นอาร์เรย์ชุดคีย์ทั้งหมดของแผนที่ ฯลฯ ) หรือดำเนินการวนซ้ำของลูปซึ่งเป็นปัจจัยคูณของขนาด A ทำไมฉันถึงพูดว่า "a multiplicative factor"? - เนื่องจาก loops และ function (เกือบตามคำนิยาม) มีเวลาทำงานแบบ multiplicative: จำนวนการวนซ้ำ, เวลาที่ทำงานเสร็จในลูป (หรือสำหรับฟังก์ชัน: จำนวนครั้งที่คุณเรียก ฟังก์ชั่นเวลาทำงานเสร็จในฟังก์ชั่น) (สิ่งนี้จะเก็บไว้ถ้าเราไม่ทำอะไรแฟนซีเช่น skip ลูปหรือออกจากลูปก่อนหรือเปลี่ยนการควบคุมการไหลในฟังก์ชั่นตามข้อโต้แย้งซึ่งเป็นเรื่องธรรมดามาก) นี่เป็นตัวอย่างของเทคนิคการสร้างภาพด้วย pseudocode ประกอบ
(ที่นี่, x
s แทนหน่วยเวลาคงที่, คำสั่งโปรเซสเซอร์, opcode ล่าม, อะไรก็ตาม)
for(i=0; i<A; i++) // A * ...
some O(1) operation // 1
--> A*1 --> O(A) time
visualization:
|<------ A ------->|
1 2 3 4 5 x x ... x
other languages, multiplying orders of growth:
javascript, O(A) time and space
someListOfSizeA.map((x,i) => [x,i])
python, O(rows*cols) time and space
[[r*c for c in range(cols)] for r in range(rows)]
ตัวอย่างที่ 2:
for every x in listOfSizeA: // A * (...
some O(1) operation // 1
some O(B) operation // B
for every y in listOfSizeC: // C * (...
some O(1) operation // 1))
--> O(A*(1 + B + C))
O(A*(B+C)) (1 is dwarfed)
visualization:
|<------ A ------->|
1 x x x x x x ... x
2 x x x x x x ... x ^
3 x x x x x x ... x |
4 x x x x x x ... x |
5 x x x x x x ... x B <-- A*B
x x x x x x x ... x |
................... |
x x x x x x x ... x v
x x x x x x x ... x ^
x x x x x x x ... x |
x x x x x x x ... x |
x x x x x x x ... x C <-- A*C
x x x x x x x ... x |
................... |
x x x x x x x ... x v
ตัวอย่างที่ 3:
function nSquaredFunction(n) {
total = 0
for i in 1..n: // N *
for j in 1..n: // N *
total += i*k // 1
return total
}
// O(n^2)
function nCubedFunction(a) {
for i in 1..n: // A *
print(nSquaredFunction(a)) // A^2
}
// O(a^3)
หากเราทำบางสิ่งที่ซับซ้อนเล็กน้อยคุณอาจจินตนาการได้ว่าเกิดอะไรขึ้น:
for x in range(A):
for y in range(1..x):
simpleOperation(x*y)
x x x x x x x x x x |
x x x x x x x x x |
x x x x x x x x |
x x x x x x x |
x x x x x x |
x x x x x |
x x x x |
x x x |
x x |
x___________________|
ที่นี่โครงร่างที่เล็กที่สุดที่คุณจำได้คือสิ่งที่สำคัญ รูปสามเหลี่ยมเป็นรูปทรงสองมิติ (0.5 A ^ 2) เช่นเดียวกับรูปสี่เหลี่ยมจตุรัสเป็นรูปทรงสองมิติ (A ^ 2) ปัจจัยคงที่ของทั้งสองที่นี่ยังคงอยู่ในอัตราส่วน asymptotic ระหว่างสองอย่างไรก็ตามเราไม่สนใจเหมือนทุกปัจจัย ... (มีความแตกต่างที่น่าเสียดายสำหรับเทคนิคนี้ที่ฉันไม่ได้เข้าไปที่นี่มันทำให้คุณเข้าใจผิด)
ของหลักสูตรนี้ไม่ได้หมายความว่าลูปและฟังก์ชั่นไม่ดี ในทางกลับกันพวกเขาเป็นกลุ่มของภาษาโปรแกรมสมัยใหม่และเรารักพวกเขา อย่างไรก็ตามเราสามารถเห็นได้ว่าวิธีที่เราถักลูปและฟังก์ชั่นและเงื่อนไขพร้อมกับข้อมูลของเรา (การควบคุมการไหล ฯลฯ ) เลียนแบบการใช้เวลาและพื้นที่ของโปรแกรมของเรา! หากการใช้เวลาและพื้นที่กลายเป็นปัญหานั่นคือเมื่อเราหันไปใช้ความฉลาดและค้นหาอัลกอริทึมหรือโครงสร้างข้อมูลที่เราไม่ได้พิจารณาเพื่อลดลำดับการเติบโตอย่างใด อย่างไรก็ตามเทคนิคการสร้างภาพข้อมูลเหล่านี้ (แม้ว่ามันจะไม่ได้ผลเสมอไป) ก็สามารถคาดเดาเรื่องไร้เดียงสาของคุณได้ในเวลาที่เลวร้ายที่สุด
นี่เป็นอีกสิ่งหนึ่งที่เราสามารถมองเห็นได้:
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x
x x x x x x x x
x x x x
x x
x
เราสามารถจัดเรียงใหม่และดูว่า O (N):
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x|x x x x x x x x|x x x x|x x|x
หรือบางทีคุณอาจทำการบันทึกข้อมูลผ่าน (N) สำหรับเวลาทั้งหมด O (N * บันทึก (N)):
<----------------------------- N ----------------------------->
^ x x x x x x x x x x x x x x x x|x x x x x x x x x x x x x x x x
| x x x x x x x x|x x x x x x x x|x x x x x x x x|x x x x x x x x
lgN x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x
| x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x
v x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x
ไม่น่าเชื่อ แต่ควรพูดถึงอีกครั้ง: หากเราทำการแฮช (เช่นการค้นหาพจนานุกรม / hashtable) นั่นเป็นปัจจัยของ O (1) มันค่อนข้างเร็ว
[myDictionary.has(x) for x in listOfSizeA]
\----- O(1) ------/
--> A*1 --> O(A)
ถ้าเราทำอะไรบางอย่างที่มีความซับซ้อนมากเช่นเดียวกับฟังก์ชันเวียนหรือขั้นตอนวิธีการหารและพิชิต, คุณสามารถใช้โททฤษฎีบท (มักจะทำงาน) หรือในกรณีที่ไร้สาระ Akra-Bazzi ทฤษฎีบท (ทำงานเกือบตลอดเวลา)คุณมองขึ้น เวลาทำงานของอัลกอริทึมของคุณบน Wikipedia
แต่โปรแกรมเมอร์ไม่คิดอย่างนี้เพราะในที่สุดสัญชาตญาณอัลกอริธึมก็กลายเป็นเรื่องที่สอง คุณจะเริ่มเขียนโค้ดบางอย่างที่ไม่มีประสิทธิภาพและคิดทันทีว่า "ฉันกำลังทำอะไรบางอย่างที่ไม่มีประสิทธิภาพอย่างไม่มีการลด " หากคำตอบคือ "ใช่" และคุณคาดหวังว่ามันจะสำคัญจริง ๆ จากนั้นคุณสามารถย้อนกลับไปและคิดเทคนิคต่าง ๆ เพื่อให้สิ่งต่าง ๆ ทำงานได้เร็วขึ้น (คำตอบคือ "ใช้ hashtable" เกือบทุกครั้ง และไม่ค่อยมีอะไรซับซ้อนไปหน่อย)
ค่าตัดจำหน่ายและความซับซ้อนของคดีโดยเฉลี่ย
นอกจากนี้ยังมีแนวคิดของ "ค่าตัดจำหน่าย" และ / หรือ "ค่าเฉลี่ยกรณี" (โปรดทราบว่าสิ่งเหล่านี้แตกต่างกัน)
กรณีเฉลี่ย : นี่ไม่ใช่แค่การใช้สัญลักษณ์บิ๊กโอสำหรับค่าที่คาดหวังของฟังก์ชันแทนที่จะเป็นตัวฟังก์ชันเอง ในกรณีปกติที่คุณพิจารณาว่าอินพุตทั้งหมดมีความเป็นไปได้ที่เท่ากันกรณีค่าเฉลี่ยจะเป็นค่าเฉลี่ยของเวลาทำงาน ตัวอย่างเช่นกับ quicksort แม้ว่ากรณีที่เลวร้ายที่สุดO(N^2)
สำหรับอินพุตที่ไม่ดีจริงๆบางกรณีค่าเฉลี่ยคือปกติO(N log(N))
(อินพุตที่ไม่ดีจริง ๆ มีจำนวนน้อยมากดังนั้นน้อยที่เราไม่สังเกตเห็นพวกเขาในกรณีทั่วไป)
ค่าตัดจำหน่ายเลวร้ายที่สุด : โครงสร้างข้อมูลบางอย่างอาจมีความซับซ้อนกรณีที่เลวร้ายที่สุดที่มีขนาดใหญ่ แต่รับประกันได้ว่าถ้าคุณทำการดำเนินการเหล่านี้จำนวนมากจำนวนงานเฉลี่ยที่คุณทำจะดีกว่ากรณีที่เลวร้ายที่สุด ตัวอย่างเช่นคุณอาจมีโครงสร้างข้อมูลที่ปกติจะใช้เวลาอย่างต่อเนื่องO(1)
เวลา อย่างไรก็ตามบางครั้งมันจะ 'สะอึก' และใช้O(N)
เวลาสำหรับการดำเนินการแบบสุ่มเพราะอาจจำเป็นต้องทำบัญชีหรือเก็บขยะหรือบางสิ่งบางอย่าง ... แต่มันสัญญากับคุณว่าถ้ามันสะอึกมันจะไม่สะอึกอีกครั้งสำหรับ N การดำเนินงานมากขึ้น ค่าใช้จ่ายกรณีที่เลวร้ายที่สุดยังคงเป็นO(N)
ต่อการดำเนินการ แต่ค่าตัดจำหน่ายสำหรับการดำเนินการจำนวนมากคือO(N)/N
=O(1)
ต่อการดำเนินงาน เนื่องจากการดำเนินการที่ยิ่งใหญ่นั้นหายากพอสมควรจำนวนงานที่ได้รับการพิจารณาเป็นครั้งคราวจำนวนมากจึงสามารถนำมาผสมผสานกับส่วนที่เหลือของงานเป็นปัจจัยคงที่ได้ เราบอกว่างานนี้ถูก "ตัดจำหน่าย" ผ่านการโทรจำนวนมากพอที่มันจะหายไปโดยไม่แสดงอาการ
การเปรียบเทียบสำหรับการวิเคราะห์ค่าตัดจำหน่าย:
คุณขับรถ ในบางครั้งคุณต้องใช้เวลา 10 นาทีในการไปที่ปั๊มน้ำมันจากนั้นใช้เวลา 1 นาทีในการเติมน้ำมันด้วยถัง หากคุณทำเช่นนี้ทุกครั้งที่คุณเดินทางไปทุกที่ที่มีรถของคุณ (ใช้เวลา 10 นาทีในการขับรถไปที่ปั๊มน้ำมันใช้เวลาสองสามวินาทีในการเติมเศษแกลลอน) มันจะไม่มีประสิทธิภาพมากนัก แต่ถ้าคุณเติมถังทุกสองสามวัน 11 นาทีที่ใช้ในการขับรถไปที่ปั๊มน้ำมันจะ "ตัดจำหน่าย" ในการเดินทางจำนวนมากพอที่คุณสามารถเพิกเฉยและแกล้งการเดินทางของคุณอาจจะนานกว่า 5%
การเปรียบเทียบระหว่างค่าเฉลี่ยกรณีและค่าตัดจำหน่ายต่ำสุด:
แม้ว่าหากคุณเป็นห่วงเหตุผลเกี่ยวกับผู้โจมตีมีเวกเตอร์อัลกอริทึมการโจมตีอื่น ๆ ที่ต้องกังวลนอกเหนือจากค่าตัดจำหน่ายและค่าเฉลี่ยกรณี)
ทั้งกรณีเฉลี่ยและค่าตัดจำหน่ายเป็นเครื่องมือที่มีประโยชน์อย่างเหลือเชื่อสำหรับการคิดและการออกแบบโดยคำนึงถึงขนาด
(ดูความแตกต่างระหว่างกรณีโดยเฉลี่ยและการวิเคราะห์ค่าตัดจำหน่ายหากสนใจหัวข้อย่อยนี้)
big-O หลายมิติ
คนส่วนใหญ่ไม่ได้ตระหนักว่ามีตัวแปรในที่ทำงานมากกว่าหนึ่งตัว ยกตัวอย่างเช่นในขั้นตอนวิธีการสตริงค้นหาอัลกอริทึมของคุณอาจใช้เวลาเช่นมันเป็นเชิงเส้นสองตัวแปรเช่นO([length of text] + [length of query])
O(N+M)
ขั้นตอนวิธีการอื่น ๆ ที่ไร้เดียงสามากขึ้นอาจจะเป็นหรือO([length of text]*[length of query])
O(N*M)
การไม่สนใจตัวแปรหลายตัวเป็นหนึ่งในการกำกับดูแลทั่วไปที่ฉันเห็นในการวิเคราะห์อัลกอริทึมและสามารถแฮนดิแคปให้คุณได้เมื่อออกแบบอัลกอริทึม
เรื่องราวทั้งหมด
โปรดทราบว่า big-O ไม่ใช่เรื่องทั้งหมด คุณสามารถเร่งความเร็วอัลกอริทึมบางอย่างได้อย่างมากโดยใช้การแคชทำให้แคชไม่สนใจหลีกเลี่ยงปัญหาคอขวดด้วยการทำงานกับ RAM แทนดิสก์ใช้การขนานหรือทำงานล่วงหน้า - เทคนิคเหล่านี้มักไม่ขึ้นอยู่กับลำดับการเติบโต สัญกรณ์ "big-O" แต่คุณมักจะเห็นจำนวนแกนในสัญกรณ์ใหญ่ -O ของอัลกอริทึมแบบขนาน
นอกจากนี้โปรดจำไว้ว่าเนื่องจากข้อ จำกัด ที่ซ่อนอยู่ในโปรแกรมของคุณคุณอาจไม่สนใจพฤติกรรมเชิงเส้นกำกับ คุณอาจทำงานกับค่าจำนวนที่ถูก จำกัด ตัวอย่างเช่น:
O(N log(N))
quicksort ที่รวดเร็ว คุณต้องการใช้การเรียงลำดับการแทรกซึ่งเกิดขึ้นกับอินพุตขนาดเล็กได้ดี สถานการณ์เหล่านี้มักจะเกิดขึ้นในอัลกอริทึมการแบ่งและพิชิตที่คุณแบ่งปัญหาออกเป็นปัญหาย่อยที่เล็กลงและเล็กลงเช่นการเรียงซ้ำแบบซ้ำการแปลงฟูริเยร์ที่รวดเร็วหรือการคูณเมทริกซ์ในทางปฏิบัติแม้ในหมู่อัลกอริทึมที่มีประสิทธิภาพเชิงเดียวกันหรือคล้ายกันบุญญาติของพวกเขาอาจเป็นจริงได้แรงหนุนจากสิ่งอื่น ๆ เช่นปัจจัยด้านประสิทธิภาพอื่น ๆ (quicksort และ mergesort มีทั้งO(N log(N))
แต่ quicksort ใช้ประโยชน์จากการแคช CPU); การพิจารณาที่ไม่ใช่ประสิทธิภาพเช่นความง่ายในการใช้งาน ไม่ว่าจะเป็นห้องสมุดที่มีอยู่และความน่าเชื่อถือและการบำรุงรักษาของห้องสมุด
โปรแกรมจะทำงานช้าลงในคอมพิวเตอร์ 500MHz เทียบกับคอมพิวเตอร์ 2GHz เราไม่ถือว่าสิ่งนี้เป็นส่วนหนึ่งของขอบเขตทรัพยากรเพราะเราคิดถึงการปรับขนาดในส่วนของทรัพยากรเครื่อง (เช่นต่อรอบนาฬิกา) ไม่ใช่ต่อวินาทีจริง อย่างไรก็ตามมีสิ่งที่คล้ายกันซึ่งสามารถ 'แอบแฝง' ส่งผลกระทบต่อประสิทธิภาพเช่นว่าคุณกำลังทำงานภายใต้การจำลองหรือว่าโค้ดคอมไพเลอร์ปรับให้เหมาะสมหรือไม่ สิ่งเหล่านี้อาจทำให้การดำเนินการพื้นฐานบางอย่างใช้เวลานานขึ้น (แม้จะสัมพันธ์กัน) หรือแม้แต่เร่งความเร็วหรือชะลอการดำเนินการบางอย่างแบบไม่แสดงอาการ (แม้แต่สัมพันธ์กัน) ผลกระทบอาจมีขนาดเล็กหรือใหญ่ระหว่างการนำไปใช้และ / หรือสภาพแวดล้อมที่แตกต่างกัน คุณเปลี่ยนภาษาหรือเครื่องเพื่อแยกงานพิเศษเล็ก ๆ น้อย ๆ ออกไหม ขึ้นอยู่กับเหตุผลอีกร้อยประการ (ความจำเป็นทักษะเพื่อนร่วมงานผลผลิตโปรแกรมเมอร์
ปัญหาดังกล่าวข้างต้นเช่นผลกระทบของการเลือกภาษาที่ใช้ในการเขียนโปรแกรมนั้นแทบไม่เคยถูกพิจารณาว่าเป็นส่วนหนึ่งของปัจจัยคงที่ (หรือควรจะเป็น) แต่ควรระวังเพราะบางครั้ง (แต่ไม่ค่อยมี) พวกเขาอาจส่งผลกระทบต่อสิ่งต่าง ๆ ตัวอย่างเช่นใน cpython การใช้คิวลำดับความสำคัญดั้งเดิมนั้นไม่เหมาะสำหรับasymptotically ( O(log(N))
แทนที่จะO(1)
เลือกแทรกหรือค้นหาตัวเลือก); คุณใช้การนำไปใช้อื่นหรือไม่ อาจจะไม่เนื่องจากการใช้ C อาจเร็วกว่าและอาจมีปัญหาอื่นที่คล้ายคลึงกันในที่อื่น มีการแลกเปลี่ยนกัน บางครั้งพวกเขามีความสำคัญและบางครั้งพวกเขาทำไม่ได้
( แก้ไข : คำอธิบาย "ภาษาอังกฤษธรรมดา" สิ้นสุดที่นี่)
คณิตศาสตร์ภาคผนวก
เพื่อความสมบูรณ์ความหมายที่ถูกต้องของโน้ตใหญ่-O จะเป็นดังนี้: f(x) ∈ O(g(x))
หมายความว่า "F เป็น asymptotically บนขอบเขตโดย const * g": ไม่สนใจทุกอย่างด้านล่างค่า จำกัด ของ x |f(x)| ≤ const * |g(x)|
มีอยู่อย่างต่อเนื่องดังกล่าวว่า (สัญลักษณ์อื่น ๆ มีดังนี้: เช่นเดียวกับO
หมายถึง means, Ω
หมายถึง vari มีรูปแบบตัวพิมพ์เล็ก: o
หมายถึง <และω
หมายถึง>.) f(x) ∈ Ɵ(g(x))
หมายถึงทั้งf(x) ∈ O(g(x))
และf(x) ∈ Ω(g(x))
(บน - และขอบเขตล่างโดย g): มีค่าคงที่บางอย่างเช่น f มักจะอยู่ใน "วง" ระหว่างconst1*g(x)
const2*g(x)
และมันเป็นคำบอกกล่าวที่แข็งแกร่งที่สุดที่คุณสามารถทำได้และเทียบเท่ากับ==
. (ขออภัยฉันเลือกที่จะชะลอการกล่าวถึงสัญลักษณ์ค่าสัมบูรณ์ถึงตอนนี้เพื่อประโยชน์ของความชัดเจนโดยเฉพาะอย่างยิ่งเพราะฉันไม่เคยเห็นค่าลบเกิดขึ้นในบริบทของวิทยาศาสตร์คอมพิวเตอร์)
ผู้คนมักจะใช้= O(...)
ซึ่งอาจเป็นสัญลักษณ์ 'comp-sci' ที่ถูกต้องมากขึ้นและถูกต้องตามกฎหมายในการใช้งานทั้งหมด "f = O (... )" ถูกอ่าน "f คือลำดับ ... / f คือ xxx-bounded โดย ... " และคิดว่า "f คือนิพจน์บางส่วนที่มี asymptotics ... " ∈ O(...)
ฉันถูกสอนให้ใช้ที่เข้มงวดมากขึ้น ∈
หมายถึง "เป็นองค์ประกอบของ" (ยังอ่านเหมือนก่อน) ในกรณีนี้โดยเฉพาะO(N²)
มีองค์ประกอบเช่น { 2 N²
, 3 N²
, 1/2 N²
, 2 N² + log(N)
, - N² + N^1.9
, ... } และมีขนาดใหญ่อนันต์ แต่ก็ยังคงเป็นชุด
O และΩไม่สมมาตร (n = O (n²) แต่n²ไม่ใช่ O (n)) แต่Ɵเป็นสมมาตรและ (เนื่องจากความสัมพันธ์เหล่านี้เป็นสกรรมกริยาและสะท้อนกลับ) Ɵดังนั้นจึงเป็นสมมาตรและสกรรมกริยาและสะท้อนกลับ และดังนั้นจึงแบ่งพาร์ติชันชุดของฟังก์ชันทั้งหมดลงใน สมดุลเรียน คลาสสมมูลคือชุดของสิ่งต่าง ๆ ที่เราคิดว่าเหมือนกัน นั่นคือการพูดให้ฟังก์ชั่นใด ๆ ที่คุณอาจจะคิดว่าคุณสามารถหาที่ยอมรับ / ไม่ซ้ำกัน 'asymptotic ตัวแทนของชั้นเรียน (โดยทั่วไปการขีด จำกัด ... ฉันคิดว่า ); คุณสามารถจัดกลุ่มฟังก์ชันทั้งหมดด้วยƟเป็น x-ish, log (x) ^ 2-ish, ฯลฯ ... โดยการละเว้นคำศัพท์ที่มีขนาดเล็ก (แต่บางครั้งคุณอาจติดอยู่กับ ฟังก์ชั่นที่ซับซ้อนมากขึ้นซึ่งเป็นชั้นเรียนแยกต่างหากแก่ตัวเอง)
=
สัญกรณ์อาจจะเป็นหนึ่งร่วมกันมากขึ้นและมีการใช้แม้ในเอกสารโดยมีชื่อเสียงระดับโลกนักวิทยาศาสตร์คอมพิวเตอร์ นอกจากนี้ก็มักจะเป็นกรณีที่ในบรรยากาศสบาย ๆ คนจะบอกว่าO(...)
เมื่อพวกเขาหมายถึงƟ(...)
; นี่เป็นความจริงทางเทคนิคเนื่องจากชุดของสิ่งต่าง ๆƟ(exactlyThis)
เป็นส่วนย่อยของO(noGreaterThanThis)
... และพิมพ์ได้ง่ายกว่า ;-)
แก้ไข: บันทึกด่วนนี่เกือบจะแน่นอนทำให้เกิดสัญกรณ์ Big O (ซึ่งเป็นขอบเขตบน) กับสัญกรณ์ Theta (ซึ่งเป็นทั้งขอบเขตบนและล่าง) จากประสบการณ์ของฉันนี่เป็นเรื่องปกติของการอภิปรายในการตั้งค่าที่ไม่ใช่เชิงวิชาการ ขออภัยในความสับสนที่เกิดขึ้น
ในหนึ่งประโยค: เมื่อขนาดของงานของคุณเพิ่มขึ้นต้องใช้เวลานานเท่าไหร่จึงจะเสร็จ
เห็นได้ชัดว่านั่นเป็นเพียงการใช้ "ขนาด" เป็นอินพุทและ "เวลาที่ใช้" เป็นเอาท์พุต - แนวคิดเดียวกันนี้ใช้หากคุณต้องการพูดคุยเกี่ยวกับการใช้หน่วยความจำ ฯลฯ
นี่คือตัวอย่างที่เรามีเสื้อยืด N ตัวที่เราต้องการให้แห้ง เราจะถือว่ามันรวดเร็วอย่างไม่น่าเชื่อในการทำให้พวกมันอยู่ในตำแหน่งที่แห้ง (เช่นปฏิสัมพันธ์กับมนุษย์นั้นเล็กน้อย) นั่นไม่ใช่กรณีในชีวิตจริงแน่นอน ...
ใช้ราวตากผ้าด้านนอก: สมมติว่าคุณมีสนามหลังบ้านขนาดใหญ่อย่างไม่สิ้นสุดการล้างแห้งในเวลา O (1) ไม่ว่าคุณจะมีมันมากแค่ไหนมันก็จะได้รับแสงแดดและอากาศบริสุทธิ์เหมือนกันดังนั้นขนาดจะไม่ส่งผลต่อเวลาการอบแห้ง
การใช้เครื่องอบผ้า: คุณใส่ 10 เสื้อในการโหลดแต่ละครั้งและจากนั้นพวกเขาเสร็จในอีกหนึ่งชั่วโมงต่อมา (ไม่ต้องสนใจตัวเลขจริงที่นี่ - พวกมันไม่เกี่ยวข้องดังนั้น) การอบแห้ง 50 เสื้อใช้เวลาประมาณ 5 ครั้งตราบเท่าที่การอบแห้ง 10 เสื้อ
การใส่ทุกอย่างไว้ในตู้เสื้อผ้า: ถ้าเราใส่ทุกอย่างไว้ในกองใหญ่และปล่อยให้ความอบอุ่นทั่วๆไปมันจะใช้เวลานานกว่าที่เสื้อกลางจะแห้ง ฉันไม่อยากเดารายละเอียด แต่ฉันคิดว่านี่เป็นอย่างน้อย O (N ^ 2) - เมื่อคุณเพิ่มภาระการซักเวลาในการอบแห้งจะเพิ่มขึ้นเร็วขึ้น
สิ่งสำคัญอย่างหนึ่งของสัญกรณ์ "บิ๊กโอ" คือมันไม่ได้บอกว่าอัลกอริทึมใดจะเร็วกว่าสำหรับขนาดที่กำหนด ใช้ hashtable (คีย์สตริง, ค่าจำนวนเต็ม) กับอาร์เรย์ของคู่ (สตริง, จำนวนเต็ม) การค้นหาคีย์ใน hashtable หรือองค์ประกอบในอาร์เรย์เร็วขึ้นหรือไม่โดยอ้างอิงจากสตริง (เช่นสำหรับอาร์เรย์ "ค้นหาองค์ประกอบแรกที่ส่วนสตริงตรงกับคีย์ที่กำหนด") แฮชเทเบิลโดยทั่วไปจะถูกตัดจำหน่าย (~ = "โดยเฉลี่ย") O (1) - เมื่อตั้งค่าแล้วมันจะต้องใช้เวลาประมาณ ในเวลาเดียวกันเพื่อค้นหารายการในตารางรายการ 100 เช่นเดียวกับในตาราง 1,000,000 รายการ การค้นหาองค์ประกอบในอาร์เรย์ (จากเนื้อหามากกว่าดัชนี) เป็นแบบเส้นตรงเช่น O (N) - โดยเฉลี่ยคุณจะต้องดูครึ่งรายการ
สิ่งนี้ทำให้ hashtable เร็วกว่าอาร์เรย์สำหรับการค้นหาหรือไม่ ไม่จำเป็น. หากคุณมีรายการเล็ก ๆ น้อย ๆ อาร์เรย์อาจจะเร็วกว่า - คุณอาจตรวจสอบสตริงทั้งหมดในเวลาที่ใช้ในการคำนวณ hashcode ของรายการที่คุณกำลังดูอยู่ อย่างไรก็ตามเมื่อชุดข้อมูลมีขนาดใหญ่ขึ้น hashtable จะเอาชนะอาร์เรย์ได้ในที่สุด
Big O อธิบายถึงขีด จำกัด สูงสุดของพฤติกรรมการเติบโตของฟังก์ชันตัวอย่างเช่นรันไทม์ของโปรแกรมเมื่ออินพุตมีขนาดใหญ่
ตัวอย่าง:
O (n): ถ้าฉันเพิ่มขนาดอินพุตเป็นสองเท่ารันไทม์จะเพิ่มเป็นสองเท่า
O (n 2 ): หากขนาดอินพุตเพิ่มขึ้นเป็นสองเท่าของรันไทม์สี่เท่ารันไทม์
O (บันทึก n): หากขนาดอินพุตเพิ่มขึ้นเป็นสองเท่ารันไทม์เพิ่มขึ้นหนึ่งรายการ
O (2 n ): หากขนาดอินพุตเพิ่มขึ้นหนึ่งรันไทม์จะเพิ่มเป็นสองเท่า
ขนาดอินพุตปกติพื้นที่ในบิตที่จำเป็นในการเป็นตัวแทนของอินพุต
สัญกรณ์บิ๊กโอเป็นส่วนใหญ่ที่ใช้โดยโปรแกรมเมอร์เป็นตัวชี้วัดโดยประมาณว่าการคำนวณ (อัลกอริทึม) จะใช้เวลานานเท่าใดในการแสดงให้สมบูรณ์ว่าเป็นฟังก์ชันของขนาดของชุดอินพุต
Big O มีประโยชน์ในการเปรียบเทียบว่าอัลกอริธึมจะขยายขนาดได้ดีเพียงใดเมื่อจำนวนอินพุตเพิ่มขึ้น
สัญกรณ์ Big O ที่แม่นยำยิ่งกว่านั้นใช้เพื่อแสดงพฤติกรรมเชิงซีมิโทติคของฟังก์ชั่น นั่นหมายความว่าฟังก์ชั่นนี้ทำงานเมื่อเข้าใกล้อนันต์
ในหลายกรณี "O" ของอัลกอริทึมจะตกอยู่ในกรณีใดกรณีหนึ่งต่อไปนี้:
Big O จะไม่สนใจปัจจัยที่ไม่ได้มีส่วนช่วยในการสร้างกราฟเส้นโค้งการเติบโตของฟังก์ชั่นเมื่อขนาดอินพุตเพิ่มขึ้นต่ออินฟินิตี้ ซึ่งหมายความว่าค่าคงที่ที่เพิ่มหรือคูณด้วยฟังก์ชันจะถูกละเว้น
Big O เป็นเพียงวิธี "Express" ด้วยตัวคุณเองในแบบทั่วไป "ต้องใช้เวลา / เนื้อที่ในการรันโค้ดของฉันเท่าไหร่"
คุณมักจะเห็น O (n), O (n 2) ), O (nlogn) และอื่น ๆ ทั้งหมดนี้เป็นเพียงวิธีในการแสดง อัลกอริทึมเปลี่ยนแปลงอย่างไร
O (n) หมายถึง Big O คือ n และตอนนี้คุณอาจคิดว่า "What is n !?" "n" คือจำนวนขององค์ประกอบ การถ่ายภาพที่คุณต้องการค้นหารายการในอาร์เรย์ คุณจะต้องดูที่แต่ละองค์ประกอบและเป็น "คุณเป็นองค์ประกอบ / รายการที่ถูกต้องหรือไม่" ในกรณีที่เลวร้ายที่สุดรายการนั้นอยู่ที่ดัชนีสุดท้ายซึ่งหมายความว่าต้องใช้เวลามากพอ ๆ กับที่มีรายการอยู่ในรายการดังนั้นเราจึงควรพูดว่า "เฮ้เฮ้ n เป็นค่าที่ยุติธรรมพอสมควร!" .
ดังนั้นคุณอาจเข้าใจว่า "n 2 " " แต่จะเจาะจงมากขึ้นเล่นกับความคิดที่ว่าคุณมีขั้นตอนวิธีการเรียงลำดับที่ง่ายที่สุด การเรียงลำดับแบบฟอง อัลกอริทึมนี้จำเป็นต้องดูรายการทั้งหมดสำหรับแต่ละรายการ
รายการของฉัน
การไหลที่นี่จะเป็น:
นี่คือ O n 2เนื่องจากคุณต้องดูรายการทั้งหมดในรายการที่มีรายการ "n" สำหรับแต่ละรายการคุณดูรายการทั้งหมดอีกครั้งสำหรับการเปรียบเทียบนี่คือ "n" ดังนั้นสำหรับทุกรายการคุณดู "n" คูณความหมาย n * n = n 2
ฉันหวังว่านี่จะง่ายอย่างที่คุณต้องการ
แต่จำไว้ว่าบิ๊กโอเป็นเพียงวิธีหนึ่งที่จะช่วยให้คุณมีเวลาและสถานที่มากขึ้น
Big O อธิบายลักษณะการขยายตัวพื้นฐานของอัลกอริทึม
มีข้อมูลจำนวนมากที่ Big O ไม่ได้บอกคุณเกี่ยวกับอัลกอริทึมที่กำหนด มันตัดไปที่กระดูกและให้ข้อมูลเฉพาะเกี่ยวกับลักษณะการปรับขนาดของอัลกอริทึมโดยเฉพาะวิธีการใช้ทรัพยากร (คิดว่าเวลาหรือหน่วยความจำ) ของอัลกอริทึมปรับขนาดเพื่อตอบสนองต่อ "ขนาดอินพุต"
พิจารณาความแตกต่างระหว่างเครื่องยนต์ไอน้ำกับจรวด พวกเขาไม่เพียง แต่มีความหลากหลายในสิ่งเดียวกัน (เช่นพูดว่าเป็นเครื่องยนต์ Prius เทียบกับเครื่องยนต์ Lamborghini) แต่เป็นระบบขับเคลื่อนชนิดต่าง ๆ ที่แกนกลางของมัน เครื่องยนต์ไอน้ำอาจเร็วกว่าจรวดของเล่น แต่เครื่องยนต์ไอน้ำลูกสูบไม่สามารถที่จะบรรลุความเร็วของยานยิงโคจร นี่เป็นเพราะระบบเหล่านี้มีคุณสมบัติการปรับขนาดที่แตกต่างกันโดยขึ้นอยู่กับความสัมพันธ์ของเชื้อเพลิงที่ต้องการ ("การใช้ทรัพยากร") เพื่อให้ได้ความเร็วที่กำหนด ("ขนาดอินพุต")
ทำไมสิ่งนี้สำคัญมาก เนื่องจากซอฟต์แวร์เกี่ยวข้องกับปัญหาที่อาจมีขนาดแตกต่างกันไปตามปัจจัยมากถึงล้านล้าน พิจารณาสักครู่ อัตราส่วนระหว่างความเร็วที่จำเป็นสำหรับการเดินทางไปยังดวงจันทร์และความเร็วในการเดินของมนุษย์น้อยกว่า 10,000: 1 และนั่นเล็กมากเมื่อเทียบกับช่วงของซอฟต์แวร์ขนาดอินพุตที่อาจต้องเผชิญ และเนื่องจากซอฟต์แวร์อาจเผชิญกับช่วงดาราศาสตร์ในขนาดอินพุตจึงมีความเป็นไปได้สำหรับความซับซ้อนของ Big O ของอัลกอริธึมมันเป็นธรรมชาติที่ขยายใหญ่ขึ้น
ลองพิจารณาตัวอย่างการเรียงแบบบัญญัติ Bubble-sort คือ O (n 2 ) ในขณะที่ merge-sort คือ O (n log n) สมมติว่าคุณมีแอปพลิเคชันการเรียงสองแอปพลิเคชัน A ซึ่งใช้การเรียงลำดับแบบบับเบิลและแอปพลิเคชัน B ซึ่งใช้การเรียงแบบผสานและสมมติว่าสำหรับขนาดอินพุตประมาณ 30 องค์ประกอบแอปพลิเคชัน A นั้นเร็วกว่า 1,000 เท่า หากคุณไม่จำเป็นต้องเรียงลำดับองค์ประกอบมากกว่า 30 รายการจะเห็นได้ชัดว่าคุณควรเลือกแอปพลิเคชัน A เนื่องจากเร็วกว่าขนาดอินพุตเหล่านี้ อย่างไรก็ตามหากคุณพบว่าคุณอาจต้องเรียงลำดับสิบล้านรายการสิ่งที่คุณคาดหวังคือแอปพลิเคชัน B จริง ๆ แล้วจบลงด้วยความเร็วกว่าแอปพลิเคชัน A นับพันเท่าในกรณีนี้ทั้งหมดเนื่องจากวิธีที่อัลกอริธึมแต่ละตัว
นี่คือ bestiary ภาษาอังกฤษธรรมดาที่ฉันมักจะใช้เมื่ออธิบายพันธุ์ทั่วไปของ Big-O
ในทุกกรณีต้องการอัลกอริทึมที่สูงกว่าในรายการเป็นลิสต์ที่ต่ำกว่าในรายการ อย่างไรก็ตามค่าใช้จ่ายในการย้ายไปยังระดับความซับซ้อนที่มีราคาแพงกว่านั้นแตกต่างกันอย่างมีนัยสำคัญ
O (1):
ไม่มีการเจริญเติบโต ไม่ว่าปัญหาใหญ่แค่ไหนคุณสามารถแก้ไขได้ในเวลาเท่ากัน นี่ค่อนข้างคล้ายคลึงกับการออกอากาศที่ใช้พลังงานปริมาณเท่ากันในการออกอากาศในระยะทางที่กำหนดโดยไม่คำนึงถึงจำนวนคนที่อยู่ในช่วงออกอากาศ
O (บันทึกn ):
ความซับซ้อนนี้เหมือนกับO (1)ยกเว้นว่ามันเลวร้ายลงเล็กน้อย สำหรับวัตถุประสงค์เชิงปฏิบัติทั้งหมดคุณสามารถพิจารณาว่านี่เป็นมาตราส่วนคงที่ที่มีขนาดใหญ่มาก ความแตกต่างในการทำงานระหว่างการประมวลผล 1 พันถึง 1,000 ล้านรายการเป็นเพียงปัจจัยหกเท่านั้น
O ( n ):
ค่าใช้จ่ายในการแก้ปัญหานั้นแปรผันตามขนาดของปัญหา หากปัญหาของคุณเพิ่มขึ้นเป็นสองเท่า เนื่องจากปัญหาส่วนใหญ่จะต้องถูกสแกนเข้ากับคอมพิวเตอร์ด้วยวิธีใดวิธีหนึ่งเช่นการป้อนข้อมูลการอ่านดิสก์หรือการรับส่งข้อมูลเครือข่ายจึงเป็นปัจจัยการปรับขนาดที่เหมาะสม
O ( n log n ):
ความซับซ้อนนี้คล้ายกับO ( n )มาก เพื่อวัตถุประสงค์ในทางปฏิบัติทั้งสองจะเทียบเท่ากัน ระดับความซับซ้อนนี้โดยทั่วไปจะยังคงพิจารณาปรับขนาดได้ โดยการปรับเปลี่ยนสมมติฐานบางอย่างอัลกอริทึมO ( n log n )สามารถเปลี่ยนเป็นอัลกอริทึมO ( n ) ยกตัวอย่างเช่นวิ่งขนาดของกุญแจที่จะช่วยลดการเรียงลำดับจากO ( n log n )จะO ( n )
O ( n 2 ):
เติบโตเป็นสี่เหลี่ยมจัตุรัสโดยที่nคือความยาวด้านข้างของสี่เหลี่ยมจัตุรัส นี่คืออัตราการเติบโตเช่นเดียวกับ "ผลกระทบเครือข่าย" ที่ทุกคนในเครือข่ายอาจรู้จักทุกคนในเครือข่าย การเจริญเติบโตมีราคาแพง โซลูชันที่ปรับขนาดได้ส่วนใหญ่ไม่สามารถใช้อัลกอริทึมที่มีระดับความซับซ้อนนี้โดยไม่ต้องใช้ยิมนาสติกที่มีความหมาย สิ่งนี้มักใช้กับความซับซ้อนพหุนามอื่น ๆ ทั้งหมด - O ( n k ) - เช่นกัน
O (2 n ):
ไม่ได้ปรับขนาด คุณไม่มีความหวังในการแก้ปัญหาใด ๆ ที่ไม่ใช่ขนาดเล็ก ที่เป็นประโยชน์สำหรับการรู้ว่าจะหลีกเลี่ยงและผู้เชี่ยวชาญเพื่อหาขั้นตอนวิธีการประมาณที่อยู่ในO ( n k )
Big O เป็นการวัดจำนวนเวลา / พื้นที่ที่อัลกอริทึมใช้เทียบกับขนาดของอินพุต
หากอัลกอริทึมคือ O (n) เวลา / พื้นที่จะเพิ่มขึ้นในอัตราเดียวกับอินพุต
หากอัลกอริทึมเป็น O (n 2 ) ดังนั้นเวลา / พื้นที่จะเพิ่มขึ้นตามอัตราของอินพุตกำลังสอง
และอื่น ๆ
คำอธิบายภาษาอังกฤษธรรมดาของ Big O คืออะไร ด้วยนิยามที่เป็นทางการเพียงเล็กน้อยเท่าที่จะเป็นไปได้และคณิตศาสตร์อย่างง่าย
คำอธิบายภาษาอังกฤษแบบธรรมดาเกี่ยวกับความต้องการสัญลักษณ์ Big-O:
เมื่อเราโปรแกรมเรากำลังพยายามแก้ปัญหา สิ่งที่เราเรียกว่าอัลกอริทึม สัญกรณ์ Big O ช่วยให้เราสามารถเปรียบเทียบประสิทธิภาพเคสที่แย่ลงของอัลกอริทึมของเราในแบบมาตรฐาน รายละเอียดฮาร์ดแวร์แตกต่างกันไปตามเวลาและการปรับปรุงในฮาร์ดแวร์สามารถลดเวลาที่ใช้อัลกอริทึมในการทำงาน แต่การเปลี่ยนฮาร์ดแวร์ไม่ได้หมายความว่าอัลกอริทึมของเราจะดีขึ้นหรือดีขึ้นเมื่อเวลาผ่านไปเนื่องจากอัลกอริทึมของเรายังคงเหมือนเดิม ดังนั้นเพื่อให้เราสามารถเปรียบเทียบอัลกอริทึมที่แตกต่างกันเพื่อตรวจสอบว่าดีกว่าหรือไม่เราใช้สัญกรณ์ Big O
คำอธิบายภาษาอังกฤษธรรมดาของสิ่งที่ Big O โน้ตคือ:
ขั้นตอนวิธีการทำงานไม่ได้ทั้งหมดในจำนวนเดียวกันของเวลาและสามารถแตกต่างกันไปขึ้นอยู่กับจำนวนของรายการในการป้อนข้อมูลซึ่งเราจะเรียกn จากนี้เราพิจารณาการวิเคราะห์กรณีที่แย่กว่านั้นหรือขอบเขตบนของเวลาทำงานเมื่อnมีขนาดใหญ่ขึ้นเรื่อย ๆ เราต้องระวังว่าnคืออะไรเพราะสัญลักษณ์บิ๊กโออ้างอิงหลายอย่าง
เป็นการยากที่จะวัดความเร็วของโปรแกรมซอฟต์แวร์และเมื่อเราลองคำตอบนั้นอาจซับซ้อนและเต็มไปด้วยข้อยกเว้นและกรณีพิเศษ นี่เป็นปัญหาใหญ่เนื่องจากข้อยกเว้นและกรณีพิเศษทั้งหมดนั้นทำให้เสียสมาธิและไม่ช่วยเหลือเมื่อเราต้องการเปรียบเทียบโปรแกรมที่แตกต่างกันสองรายการกับอีกรายการหนึ่งเพื่อค้นหาว่ารายการใดเป็น "เร็วที่สุด"
จากความซับซ้อนที่ไม่มีประโยชน์ทั้งหมดนี้ผู้คนพยายามอธิบายความเร็วของโปรแกรมซอฟต์แวร์โดยใช้นิพจน์ที่มีขนาดเล็กที่สุดและซับซ้อนน้อยที่สุดเท่าที่จะเป็นไปได้ การแสดงออกเหล่านี้เป็นการประมาณคร่าวๆอย่างมาก: ถึงแม้ว่าโชคดีนิดหน่อยพวกเขาจะบันทึก "แก่นแท้" ของว่าชิ้นส่วนของซอฟต์แวร์นั้นเร็วหรือช้า
เนื่องจากมันเป็นค่าประมาณเราจึงใช้ตัวอักษร "O" (Big Oh) ในการแสดงออกเพื่อเป็นการส่งสัญญาณไปยังผู้อ่านว่าเรากำลังสร้างการอนุมานขั้นต้น (และเพื่อให้แน่ใจว่าไม่มีใครผิดพลาดคิดว่าการแสดงออกนั้นถูกต้องในทางใดทางหนึ่ง)
หากคุณอ่านคำว่า "โอ้" ตามความหมาย "ตามลำดับ" หรือ "โดยประมาณ" คุณจะไม่ผิดไปมากนัก (ฉันคิดว่าการเลือกบิ๊กโอ้อาจเป็นเรื่องตลกได้)
สิ่งเดียวที่นิพจน์ "บิ๊กโอ" เหล่านี้พยายามทำคือการอธิบายว่าซอฟต์แวร์ช้าลงเพียงใดเมื่อเราเพิ่มปริมาณข้อมูลที่ซอฟต์แวร์ต้องดำเนินการ หากเราเพิ่มปริมาณข้อมูลที่ต้องประมวลผลเป็นสองเท่าซอฟต์แวร์ต้องใช้สองเท่าเพื่อให้ทำงานได้หรือไม่ สิบครั้งนานไหม ในทางปฏิบัติมีการแสดงออกที่โอ๋จำนวน จำกัด มากที่คุณจะต้องเผชิญและต้องกังวลเกี่ยวกับ:
ดี:
O(1)
คงที่ : โปรแกรมใช้เวลาเดียวกันในการรันไม่ว่าอินพุตจะใหญ่แค่ไหนO(log n)
ลอการิทึม : รันไทม์ของโปรแกรมเพิ่มขึ้นอย่างช้าๆเท่านั้นถึงแม้จะมีขนาดใหญ่ขึ้นของอินพุตไม่ดี:
O(n)
เป็น Linear : เวลาทำงานของโปรแกรมเพิ่มขึ้นตามขนาดของอินพุตO(n^k)
พหุนาม : - เวลาการประมวลผลเติบโตเร็วขึ้นและเร็วขึ้น - เป็นฟังก์ชันพหุนาม - เมื่อขนาดของอินพุตเพิ่มขึ้น... และสิ่งที่น่าเกลียด:
O(k^n)
เอ็กซ์โพเนนเชียลรันไทม์ของโปรแกรมเพิ่มขึ้นอย่างรวดเร็วโดยเพิ่มขนาดของปัญหาให้พอสมควร - เป็นประโยชน์เฉพาะกับการประมวลผลชุดข้อมูลขนาดเล็กที่มีอัลกอริธึมอธิบายO(n!)
แฟกทอเรียลเวลาทำงานของโปรแกรมจะนานกว่าที่คุณจะสามารถรอได้ แต่ชุดข้อมูลที่มีขนาดเล็กที่สุดและดูธรรมดาที่สุดO(n log n)
ซึ่งก็ถือว่าดี
คำตอบที่ตรงไปตรงมาง่ายๆคือ:
Big O แสดงเวลา / พื้นที่ที่เลวร้ายที่สุดที่เป็นไปได้สำหรับอัลกอริทึมนั้น อัลกอริทึมจะไม่ใช้พื้นที่ / เวลามากกว่าที่กำหนดไว้ Big O แสดงถึงความซับซ้อนของเวลา / พื้นที่ในกรณีที่รุนแรง
ตกลง 2 เซ็นต์ของฉัน
Big-O คืออัตราการเพิ่มขึ้นของทรัพยากรที่ใช้โดยโปรแกรม wrt problem-instance-size
ทรัพยากร: อาจเป็นเวลาทั้งหมดของ CPU อาจเป็นพื้นที่แรมสูงสุด โดยค่าเริ่มต้นหมายถึงเวลาของ CPU
บอกว่าปัญหาคือ "ค้นหาผลรวม"
int Sum(int*arr,int size){
int sum=0;
while(size-->0)
sum+=arr[size];
return sum;
}
problem-instance = {5,10,15} ==> problem-instance-size = 3, iterations-in-loop = 3
problem-instance = {5,10,15,20,25} ==> problem-instance-size = 5 การวนซ้ำในวงวน = 5
สำหรับอินพุตของขนาด "n" โปรแกรมจะเพิ่มขึ้นที่ความเร็วของการวนซ้ำ "n" ในอาเรย์ ดังนั้น Big-O คือ N แสดงเป็น O (n)
บอกว่าปัญหาคือ "ค้นหาชุดค่าผสม"
void Combination(int*arr,int size)
{ int outer=size,inner=size;
while(outer -->0) {
inner=size;
while(inner -->0)
cout<<arr[outer]<<"-"<<arr[inner]<<endl;
}
}
problem-instance = {5,10,15} ==> problem-instance-size = 3, total-iterations = 3 * 3 = 9
problem-instance = {5,10,15,20,25} ==> problem-instance-size = 5, ซ้ำทั้งหมด = 5 * 5 = 25
สำหรับอินพุตของขนาด "n" โปรแกรมจะเพิ่มขึ้นที่ความเร็วของการวนซ้ำ "n * n" ในอาเรย์ ดังนั้น Big-O คือ N 2แสดงเป็น O (n 2 )
สัญลักษณ์ Big O เป็นวิธีการอธิบายขอบเขตบนของอัลกอริทึมในแง่ของพื้นที่หรือเวลาทำงาน n คือจำนวนองค์ประกอบในปัญหา (เช่นขนาดของอาเรย์จำนวนโหนดในทรีเป็นต้น) เราสนใจที่จะอธิบายเวลาทำงานเมื่อเอ็นโตขึ้น
เมื่อเราบอกว่าอัลกอริทึมบางตัวคือ O (f (n)) เรากำลังบอกว่าเวลาที่ใช้ (หรือพื้นที่ที่ต้องการ) โดยอัลกอริทึมนั้นจะต่ำกว่าค่าคงที่บางครั้งเสมอ f (n)
การบอกว่าการค้นหาแบบไบนารีมีเวลาทำงานของ O (logn) คือการบอกว่ามีค่าคงที่บางค่าซึ่งคุณสามารถคูณ log (n) โดยที่จะใหญ่กว่าเวลาทำงานของการค้นหาแบบไบนารีเสมอ ในกรณีนี้คุณจะมีปัจจัยคงที่ของการเปรียบเทียบบันทึก (n) เสมอ
ในคำอื่น ๆ ที่ g (n) เป็นเวลาทำงานของอัลกอริทึมของคุณเราบอกว่า g (n) = O (f (n)) เมื่อ g (n) <= c * f (n) เมื่อ n> k โดยที่ c และ k เป็นค่าคงที่
" คำอธิบายภาษาอังกฤษธรรมดาของ Big O คืออะไรคำจำกัดความที่เป็นทางการที่สุดเท่าที่จะเป็นไปได้และคณิตศาสตร์ง่าย ๆ "
คำถามที่เรียบง่ายและสวยงามเช่นนั้นดูเหมือนว่าอย่างน้อยก็ควรได้รับคำตอบสั้น ๆ เช่นนักเรียนอาจได้รับในระหว่างการติว
สัญกรณ์ Big O เพียงบอกเวลาเท่าไร * อัลกอริทึมสามารถทำงานภายในได้ในแง่ของจำนวนข้อมูลอินพุตเท่านั้น **
(* ในความรู้สึกที่ยอดเยี่ยมของหน่วยเวลา!)
(** ซึ่งเป็นเรื่องสำคัญเพราะผู้คนมักจะต้องการมากขึ้นไม่ว่าพวกเขาจะมีชีวิตอยู่ในวันนี้หรือวันพรุ่งนี้)
ดีมากเกี่ยวกับสัญกรณ์ Big O ถ้านั่นคือสิ่งที่มันทำ?
จวนพูดวิเคราะห์ Big O คือมีประโยชน์และมีความสำคัญเพราะ Big O ทำให้โฟกัสเต็มที่ในขั้นตอนวิธีการของตัวเองความซับซ้อนและสมบูรณ์ละเว้นสิ่งที่เป็นเพียงสัดส่วนคงที่เหมือนเป็นเครื่องมือ JavaScript ความเร็วของ CPU ที่เชื่อมต่ออินเทอร์เน็ตของคุณและ ทุกสิ่งที่จะกลายเป็นอย่างรวดเร็วกลายเป็นล้าสมัยหาญเป็นรุ่นT Big O มุ่งเน้นไปที่การแสดงในวิธีที่มีความสำคัญเท่า ๆ กันกับผู้คนที่อาศัยอยู่ในปัจจุบันหรือในอนาคต
Big O สัญกรณ์ยังส่องสปอตไลโดยตรงบนหลักการที่สำคัญที่สุดของการเขียนโปรแกรมคอมพิวเตอร์ / วิศวกรรมความเป็นจริงที่เป็นแรงบันดาลใจเขียนโปรแกรมที่ดีที่จะทำให้ความคิดและความฝัน: วิธีเดียวที่จะบรรลุผลเกินเดือนมีนาคมไปข้างหน้าช้าของเทคโนโลยีคือการคิดค้นที่ดีกว่า ขั้นตอนวิธี
ตัวอย่างอัลกอริทึม (Java):
// given a list of integers L, and an integer K
public boolean simple_search(List<Integer> L, Integer K)
{
// for each integer i in list L
for (Integer i : L)
{
// if i is equal to K
if (i == K)
{
return true;
}
}
return false;
}
คำอธิบายอัลกอริทึม:
อัลกอริทึมนี้ค้นหารายการ, รายการตามรายการ, ค้นหาคีย์,
วนซ้ำแต่ละรายการในรายการถ้าเป็นกุญแจแล้วส่งคืนค่า True
หากลูปเสร็จสิ้นโดยไม่ค้นหาคีย์ให้ส่งคืน False
สัญลักษณ์ Big-O แสดงถึงขอบบนของความซับซ้อน (เวลา, อวกาศ, .. )
วิธีค้นหาความซับซ้อนของเวลาบน Big-O:
คำนวณว่าใช้เวลาเท่าใด (เกี่ยวกับขนาดอินพุต) กรณีที่แย่ที่สุดใช้เวลา:
กรณีที่เลวร้ายที่สุด: กุญแจไม่มีอยู่ในรายการ
เวลา (กรณีที่เลวร้ายที่สุด) = 4n + 1
เวลา: O (4n + 1) = O (n) | ใน Big-O ค่าคงที่จะถูกเพิกเฉย
O (n) ~ เป็นเส้นตรง
นอกจากนี้ยังมี Big-Omega ซึ่งแสดงถึงความซับซ้อนของ Best-Case:
กรณีที่ดีที่สุด: กุญแจคือรายการแรก
เวลา (กรณีที่ดีที่สุด) = 4
เวลา: Ω (4) = O (1) ~ ทันที \ คงที่
C
น่าจะดีกว่า
บิ๊กโอ
f (x) = O ( g (x)) เมื่อ x ไปที่ a (เช่น a = + ∞) หมายความว่ามีฟังก์ชันkเช่นนั้น:
f (x) = k (x) g (x)
k ถูกล้อมรอบในละแวกใกล้เคียงของ a (ถ้า a = + ∞นี่หมายความว่ามีตัวเลข N และ M เช่นนั้นสำหรับทุกๆ x> N, | k (x) | <M)
กล่าวอีกนัยหนึ่งในภาษาอังกฤษธรรมดา: f (x) = O ( g (x)), x → a, หมายความว่าในย่าน a, fย่อยสลายเป็นผลิตภัณฑ์ของgและฟังก์ชั่นขอบเขตบางอย่าง
เล็ก ๆ
โดยวิธีการที่นี่เพื่อเปรียบเทียบความหมายของ o ขนาดเล็ก
f (x) = o ( g (x)) เมื่อ x ไปถึงหมายความว่ามีฟังก์ชัน k เช่นนั้น:
f (x) = k (x) g (x)
k (x) ไปที่ 0 เมื่อ x ไปที่ a
ตัวอย่าง
บาป x = O (x) เมื่อ x → 0
บาป x = O (1) เมื่อ x → + ∞,
x 2 + x = O (x) เมื่อ x → 0
x 2 + x = O (x 2 ) เมื่อ x → + ∞
ln (x) = o (x) = O (x) เมื่อ x → + ∞
ความสนใจ! สัญกรณ์ที่มีเครื่องหมายเท่ากับ "=" ใช้ "ความเสมอภาคปลอม": มันเป็นความจริงที่ o (g (x)) = O (g (x)) แต่เป็นเท็จว่า O (g (x)) = o (g (x)) ในทำนองเดียวกันมันก็โอเคที่จะเขียน "ln (x) = o (x) เมื่อ x → + ∞" แต่สูตร "o (x) = ln (x)" จะไม่มีเหตุผล
ตัวอย่างเพิ่มเติม
O (1) = O (n) = O (n 2 ) เมื่อ n → + ∞ (แต่ไม่ใช่วิธีอื่น ๆ ความเท่าเทียมกันคือ "ปลอม"),
O (n) + O (n 2 ) = O (n 2 ) เมื่อ n → + ∞
O (O (n 2 )) = O (n 2 ) เมื่อ n → + ∞
O (n 2 ) O (n 3 ) = O (n 5 ) เมื่อ n → + ∞
นี่คือบทความ Wikipedia: https://en.wikipedia.org/wiki/Big_O_notation
สัญกรณ์ Big O เป็นวิธีการอธิบายว่าอัลกอริทึมจะทำงานได้เร็วเพียงใดโดยกำหนดจำนวนพารามิเตอร์อินพุตที่เราจะเรียกว่า "n" มันมีประโยชน์ในด้านวิทยาศาสตร์คอมพิวเตอร์เพราะเครื่องต่าง ๆ ทำงานด้วยความเร็วที่ต่างกันและเพียงบอกว่าอัลกอริทึมใช้เวลา 5 วินาทีไม่ได้บอกอะไรคุณมากนักในขณะที่คุณใช้ระบบที่มีโปรเซสเซอร์ Octo-Core 4.5 Ghz ฉันอาจใช้งานได้ ระบบ 15 800 Mhz อายุ 15 ปีซึ่งอาจใช้เวลานานกว่าโดยไม่คำนึงถึงอัลกอริทึม ดังนั้นแทนที่จะระบุว่าอัลกอริทึมทำงานเร็วแค่ไหนในแง่ของเวลาเราบอกว่ามันเร็วแค่ไหนในแง่ของจำนวนพารามิเตอร์อินพุตหรือ "n" ด้วยการอธิบายอัลกอริทึมด้วยวิธีนี้เราสามารถเปรียบเทียบความเร็วของอัลกอริทึมโดยไม่ต้องคำนึงถึงความเร็วของคอมพิวเตอร์เอง
ไม่แน่ใจว่าฉันมีส่วนร่วมในเรื่องนี้ต่อไป แต่ยังคิดว่าฉันจะแบ่งปัน: ฉันเคยพบโพสต์บล็อกนี้มีคำอธิบายและตัวอย่างที่เป็นประโยชน์ (แม้ว่าจะธรรมดามาก) และตัวอย่างใน Big O:
จากตัวอย่างนี้ช่วยให้เข้าใจพื้นฐานของกะโหลกศีรษะเหมือนกระดองเต่าของฉันดังนั้นฉันคิดว่าการอ่านโคตร 10 นาทีเพื่อให้คุณไปในทิศทางที่ถูกต้อง
คุณต้องการที่จะรู้ว่าทุกสิ่งที่มีการรู้ของ O ใหญ่? ก็เช่นกัน
ดังนั้นการพูดคุยของ O ใหญ่ฉันจะใช้คำที่มีเพียงหนึ่งจังหวะในพวกเขา หนึ่งเสียงต่อคำ คำเล็ก ๆ นั้นรวดเร็ว คุณก็รู้คำศัพท์เหล่านี้และฉันก็เช่นกันเราจะใช้คำศัพท์ด้วยเสียงเดียว พวกเขามีขนาดเล็ก ฉันแน่ใจว่าคุณจะรู้ทุกคำที่เราจะใช้!
ทีนี้มาพูดกับคุณกันเถอะ ส่วนใหญ่ฉันไม่ชอบงาน คุณชอบทำงานไหม อาจเป็นกรณีที่คุณทำ แต่ฉันแน่ใจว่าฉันไม่ได้
ฉันไม่ชอบไปทำงาน ฉันไม่ชอบที่จะใช้เวลาในที่ทำงาน ถ้าฉันมีวิธีของฉันฉันต้องการที่จะเล่นและทำสิ่งที่สนุก คุณรู้สึกเหมือนฉันไหม
ในบางครั้งฉันต้องไปทำงาน มันเศร้า แต่จริง ดังนั้นเมื่อฉันทำงานฉันมีกฎ: ฉันพยายามทำงานให้น้อยลง เหมือนกับที่ฉันไม่มีงานทำ จากนั้นฉันก็ไปเล่น!
ดังนั้นนี่คือข่าวใหญ่: O ใหญ่สามารถช่วยฉันไม่ให้ทำงาน! ฉันสามารถเล่นได้มากขึ้นถ้าฉันรู้ว่าโอใหญ่ทำงานน้อยกว่าเล่นมากขึ้น! นั่นคือสิ่งที่โอใหญ่ช่วยฉันได้
ตอนนี้ฉันมีงาน ฉันมีรายการนี้: หนึ่ง, สอง, สาม, สี่, ห้า, หก ฉันต้องเพิ่มทุกสิ่งในรายการนี้
ว้าวฉันเกลียดงาน แต่โอ้ดีฉันต้องทำสิ่งนี้ ดังนั้นฉันไปเลย
หนึ่งบวกสองคือสาม…บวกสามคือหก ... และสี่คือ ... ฉันไม่รู้ ฉันหลงทาง. มันยากเกินไปสำหรับฉันที่จะทำในหัวของฉัน ฉันไม่สนใจงานประเภทนี้มากนัก
ดังนั้นเราจะไม่ทำงาน ให้คุณและฉันแค่คิดว่ามันยากแค่ไหน ฉันต้องทำงานมากแค่ไหนเพื่อเพิ่มตัวเลขหกตัว?
เรามาดูกันดีกว่า ฉันต้องเพิ่มหนึ่งและสองจากนั้นเพิ่มที่สามแล้วเพิ่มที่สี่…ทั้งหมดในทั้งหมดฉันนับหกเพิ่ม ฉันต้องเพิ่มหกตัวเพื่อแก้ปัญหานี้
นี่มาใหญ่ O, เพื่อบอกเราว่าคณิตศาสตร์นี้ยากแค่ไหน
Big O พูดว่า: เราต้องเพิ่มหกตัวเพื่อแก้ปัญหานี้ หนึ่งเพิ่มสำหรับแต่ละสิ่งจากหนึ่งถึงหก ชิ้นงานเล็ก ๆ หกชิ้น ... ชิ้นงานแต่ละชิ้นเพิ่มได้หนึ่งชิ้น
ฉันจะไม่เพิ่มงานลงในตอนนี้ แต่ฉันรู้ว่ามันยากแค่ไหน มันจะเป็นหกเสริม
โอ้ไม่ตอนนี้ฉันมีงานมากขึ้น Sheesh ใครเป็นคนทำเรื่องแบบนี้!
ตอนนี้พวกเขาขอให้ฉันเพิ่มจากหนึ่งถึงสิบ! ทำไมฉันต้องทำอย่างนั้น? ฉันไม่ต้องการเพิ่มหนึ่งถึงหก หากต้องการเพิ่มจากหนึ่งถึงสิบ…ดี…ที่จะยิ่งยากขึ้น!
มันจะยากกว่านี้อีกไหม? ฉันจะต้องทำงานอีกมาก ฉันต้องการขั้นตอนมากหรือน้อย
ฉันเดาว่าฉันจะต้องเพิ่มสิบตัว…หนึ่งอันสำหรับแต่ละเรื่องจากหนึ่งถึงสิบ สิบมากกว่าหก ฉันจะต้องทำงานที่เพิ่มมากขึ้นจากหนึ่งถึงสิบกว่าหนึ่งถึงหก!
ฉันไม่ต้องการเพิ่มในตอนนี้ ฉันแค่อยากจะคิดว่ามันอาจจะยากขนาดนั้น และฉันหวังว่าจะเล่นโดยเร็วที่สุด
ในการเพิ่มจากหนึ่งถึงหกนั่นเป็นงานบางส่วน แต่คุณเห็นไหมว่าการเพิ่มจากหนึ่งถึงสิบนั้นเป็นงานที่มากขึ้นหรือไม่
Big O คือเพื่อนและของคุณ Big O ช่วยให้เราคิดว่าเราต้องทำงานมากแค่ไหนเพื่อให้เราสามารถวางแผนได้ และถ้าเราเป็นเพื่อนกับโอใหญ่เขาสามารถช่วยเราเลือกงานที่ไม่ยาก!
ตอนนี้เราต้องทำงานใหม่ ไม่นะ. ฉันไม่ชอบสิ่งนี้เลย
งานใหม่คือ: เพิ่มทุกสิ่งจากหนึ่งไปยัง n
รอ! n คืออะไร ฉันคิดถึงเรื่องนั้นเหรอ? ฉันจะเพิ่มจากหนึ่งไปอีก n ได้อย่างไรถ้าคุณไม่บอกฉันว่า n คืออะไร?
ฉันไม่รู้ว่า n คืออะไร ฉันไม่ได้บอก คุณเป็นอย่างไร ไม่มี? โอ้ดี ดังนั้นเราไม่สามารถทำงานได้ ต๊าย
แต่ถึงแม้ว่าเราจะไม่ทำงานตอนนี้เราสามารถเดาได้ว่ามันจะยากแค่ไหนถ้าเรารู้ n เราจะต้องรวมสิ่งต่างๆเข้าด้วยกันใช่ไหม? แน่นอน!
ตอนนี้ที่นี่มาใหญ่ O และเขาจะบอกเราว่างานนี้ยาก เขาพูดว่า: การเพิ่มทุกสิ่งจากหนึ่งไปยัง N หนึ่งต่อหนึ่งคือ O (n) ในการเพิ่มสิ่งเหล่านี้ [ฉันรู้ว่าฉันต้องเพิ่ม n ครั้ง] [1] นั่นคือโอใหญ่! เขาบอกให้เรารู้ว่าการทำงานบางประเภทนั้นยากแค่ไหน
สำหรับฉันฉันคิดว่าโอใหญ่เหมือนเจ้านายใหญ่ช้า เขาคิดในการทำงาน แต่เขาไม่ได้ทำ เขาอาจพูดว่า "งานนั้นเร็วมาก" หรือเขาอาจพูดว่า "งานนั้นช้าและยากมาก!" แต่เขาไม่ได้ทำงาน เขาแค่ดูที่งานจากนั้นเขาก็บอกเราว่าต้องใช้เวลาเท่าไร
ฉันสนใจล็อตใหญ่ ๆ ทำไม ฉันไม่ชอบทำงาน! ไม่มีใครชอบทำงาน นั่นคือเหตุผลที่เราทุกคนรักโอใหญ่! เขาบอกเราว่าเราทำงานได้เร็วแค่ไหน เขาช่วยให้เราคิดว่าการทำงานหนักเป็นอย่างไร
เอ่อทำงานมากขึ้น ตอนนี้เราจะไม่ทำงาน แต่ลองทำแผนทีละขั้นตอน
พวกเขาให้ไพ่สำรับสิบใบแก่เรา พวกเขาทั้งหมดปะปนกัน: เจ็ดสี่สี่หก…ไม่ตรงเลย และตอนนี้ ... งานของเราคือการจัดเรียงพวกเขา
Ergh ฟังดูเหมือนทำงานมาก!
เราจะจัดเรียงสำรับนี้อย่างไร ฉันมีแผน
ฉันจะดูไพ่แต่ละคู่จับคู่ผ่านเด็คตั้งแต่ต้นจนจบ หากไพ่ใบแรกในหนึ่งคู่มีขนาดใหญ่และบัตรใบถัดไปในคู่นั้นมีขนาดเล็กฉันจะสลับการ์ดเหล่านั้น อื่นฉันไปที่คู่ต่อไปและอื่น ๆ ... และในไม่ช้าดาดฟ้าเสร็จ
เมื่อดาดฟ้าเสร็จฉันถาม: ฉันแลกเปลี่ยนการ์ดในบัตรนั้นหรือไม่? ถ้าเป็นเช่นนั้นฉันต้องทำทั้งหมดอีกครั้งจากด้านบน
ในบางจุดในบางครั้งจะไม่มีการแลกเปลี่ยนและการเรียงลำดับของดาดฟ้าของเราจะทำ ทำงานเยอะมาก!
ทีนี้มันจะทำงานได้มากแค่ไหนในการจัดเรียงไพ่ตามกฎเหล่านั้น?
ฉันมีไพ่สิบใบ และส่วนใหญ่เวลา - นั่นคือถ้าฉันไม่มีโชคมากมาย - ฉันต้องผ่านทั้งเด็คถึงสิบครั้งโดยมีไพ่มากถึงสิบครั้งในแต่ละครั้งที่เด็ค
บิ๊กโอช่วยฉันด้วย!
Big O เข้ามาและพูดว่า: สำหรับสำรับไพ่ n ใบเพื่อเรียงลำดับวิธีนี้จะกระทำในเวลา O (N กำลังสอง)
ทำไมเขาถึงพูดว่า n กำลังสอง?
คุณก็รู้ว่า n กำลังสองคือ n คูณ n ตอนนี้ฉันเข้าใจแล้ว: ตรวจสอบการ์ด n ใบถึงสิ่งที่อาจจะ n ครั้งผ่านสำรับ นั่นคือสองลูปแต่ละอันมี n ก้าว นั่นคืองานที่ต้องทำมากมาย ทำงานเยอะแน่นอน!
ตอนนี้เมื่อ O ใหญ่บอกว่าจะใช้งาน O (n กำลังสอง) เขาไม่ได้หมายความว่าเพิ่ม n squared บนจมูก มันอาจจะน้อยไปหน่อยสำหรับบางกรณี แต่ในกรณีที่เลวร้ายที่สุดมันจะอยู่ใกล้กับขั้นตอนการทำงานเพื่อจัดเรียงดาดฟ้า
ตอนนี้ที่นี่คือที่ที่ O ใหญ่เป็นเพื่อนของเรา
บิ๊กโอชี้ให้เห็นสิ่งนี้: เมื่อ n เริ่มมีขนาดใหญ่เมื่อเราจัดเรียงไพ่งานจะได้รับมากขึ้นกว่างานที่เพิ่งเพิ่มเข้ามา เราจะรู้สิ่งนี้ได้อย่างไร
ทีนี้ถ้า n ใหญ่จริงเราไม่สนใจว่าเราจะบวก n หรือ n กำลังสอง
สำหรับใหญ่ n, n กำลังสองนั้นใหญ่กว่า n
Big O บอกเราว่าการจัดเรียงสิ่งต่าง ๆ นั้นยากกว่าการเพิ่มสิ่งต่าง ๆ O (n กำลังสอง) มากกว่า O (n) สำหรับใหญ่ n นั่นหมายความว่าถ้า n มีขนาดใหญ่จริงการเรียงลำดับของสิ่งต่าง ๆ n ต้องใช้เวลามากกว่าการเพิ่ม n สิ่งต่าง ๆ
Big O ไม่ได้ช่วยแก้ปัญหาให้เรา Big O บอกเราว่างานหนักแค่ไหน
ฉันมีไพ่หนึ่งใบ ฉันจัดเรียงพวกเขา คุณช่วย ขอบคุณ
มีวิธีที่รวดเร็วกว่าในการจัดเรียงไพ่หรือไม่ ใหญ่ O ช่วยเราได้ไหม
ใช่มีวิธีที่เร็วกว่ามาก! ใช้เวลาพอสมควรในการเรียนรู้ แต่ใช้งานได้ ... และทำงานได้ค่อนข้างเร็ว คุณสามารถลองได้เช่นกัน แต่ใช้เวลาในแต่ละขั้นตอนและไม่เสียสถานที่
ด้วยวิธีใหม่ในการจัดเรียงเด็คเราไม่ได้ตรวจสอบไพ่คู่เดียวกับที่เราทำเมื่อไม่นานมานี้ นี่คือกฎใหม่ของคุณในการจัดเรียงสำรับนี้:
หนึ่ง: ฉันเลือกไพ่หนึ่งใบในส่วนของเด็คเราทำงานตอนนี้ คุณสามารถเลือกหนึ่งสำหรับฉันถ้าคุณต้องการ (ครั้งแรกที่เราทำเช่นนี้“ ส่วนหนึ่งของเด็คที่เราทำอยู่ตอนนี้” เป็นเด็คทั้งหมดแน่นอน)
สอง: ฉันสาดดาดฟ้าบนการ์ดที่คุณเลือก สิ่งนี้คืออะไร ฉันจะสเปรย์ได้อย่างไร ดีฉันไปจากบัตรเริ่มต้นทีละคนและฉันมองหาการ์ดที่สูงกว่าบัตรสเปรย์
สาม: ฉันไปจากการ์ดใบสุดท้ายและฉันมองหาการ์ดที่ต่ำกว่าการ์ดสเปรย์
เมื่อฉันพบไพ่สองใบนี้แล้วฉันจะสลับมันและไปหาการ์ดเพิ่มเติมเพื่อสลับ นั่นคือฉันกลับไปที่ขั้นตอนที่สองและแผ่บนการ์ดที่คุณเลือกเพิ่มเติม
ในบางจุดการวนซ้ำนี้ (จากสองถึงสาม) จะสิ้นสุดลง จะสิ้นสุดลงเมื่อทั้งสองส่วนของการค้นหานี้พบกันที่สเปรย์การ์ด จากนั้นเราเพิ่งสาดดาดฟ้ากับการ์ดที่คุณเลือกในขั้นตอนที่หนึ่ง ตอนนี้การ์ดทั้งหมดที่อยู่ใกล้จุดเริ่มต้นจะต่ำกว่าการ์ดสเปรย์ และการ์ดที่ใกล้ที่สุดจะสูงกว่าการ์ดสเปรย์ เคล็ดลับเด็ด!
สี่ (และนี่คือส่วนที่สนุก): ตอนนี้ฉันมีชั้นเล็ก ๆ สองชั้นต่ำกว่าสเปรย์การ์ดอีกหนึ่งใบและอีกหนึ่งใบก็จะสูงขึ้นอีกหนึ่งใบ ตอนนี้ฉันไปที่ขั้นตอนที่หนึ่งบนสำรับเล็ก ๆ ! กล่าวคือฉันเริ่มต้นจากขั้นตอนที่หนึ่งบนเด็คเล็ก ๆ แรกและเมื่องานนั้นเสร็จฉันเริ่มจากขั้นตอนที่หนึ่งบนเด็คเล็ก ๆ ถัดไป
ฉันแยกชิ้นส่วนออกเป็นชิ้น ๆ แล้วเรียงลำดับชิ้นส่วนเล็กและเล็กมากขึ้นและในบางครั้งฉันก็ไม่มีงานทำอีกต่อไป ตอนนี้อาจดูช้าไปด้วยกฎทั้งหมด แต่เชื่อใจฉันมันไม่ได้ช้าเลย มันทำงานน้อยกว่าวิธีแรกในการจัดเรียงสิ่งต่าง ๆ !
ประเภทนี้เรียกว่าอะไร? เรียกว่า Quick Sort! การเรียงลำดับนั้นทำโดยชายคนหนึ่งที่ชื่อCAR Hoareและเขาเรียกมันว่า Quick Sort ตอนนี้ Quick Sort จะถูกใช้งานตลอดเวลา!
Quick Sort แบ่งออกเป็นชั้นขนาดใหญ่ในขนาดเล็ก กล่าวคือมันแบ่งงานใหญ่ ๆ เป็นงานเล็ก ๆ
อืมม ฉันคิดว่าอาจมีกฎอยู่ในนั้น เพื่อทำให้งานใหญ่เล็กลง
การเรียงลำดับนี้ค่อนข้างรวดเร็ว เร็วแค่ไหน? Big O บอกเราว่าการเรียงลำดับนี้ต้องใช้งาน O (n log n) ในกรณีนี้
มันเร็วกว่าหรือน้อยกว่าการเรียงลำดับแรกหรือไม่ บิ๊กโอโปรดช่วยด้วย!
การเรียงลำดับแรกคือ O (n กำลังสอง) แต่ Quick Sort คือ O (n log n) คุณรู้ไหมว่า n log n น้อยกว่า n กำลังสองสำหรับ big n ใช่ไหม นั่นคือวิธีที่เรารู้ว่า Quick Sort นั้นรวดเร็ว!
หากคุณต้องเรียงลำดับดาดฟ้าวิธีที่ดีที่สุดคืออะไร? คุณสามารถทำสิ่งที่คุณต้องการได้ แต่ฉันจะเลือก Quick Sort
ทำไมฉันถึงเลือก Quick Sort ฉันไม่ชอบที่จะทำงานแน่นอน! ฉันต้องการทำงานให้เสร็จเร็วที่สุดที่จะทำได้
ฉันจะรู้ได้อย่างไรว่า Quick Sort ทำงานได้น้อยลง ฉันรู้ว่า O (n log n) น้อยกว่า O (n กำลังสอง) O มีขนาดเล็กกว่าดังนั้น Quick Sort จึงทำงานน้อยลง!
ตอนนี้คุณรู้จักเพื่อนของฉันแล้วบิ๊กโอเขาช่วยให้เราทำงานน้อยลง และถ้าคุณรู้จักโอใหญ่คุณก็สามารถทำงานได้น้อยลงเช่นกัน!
คุณเรียนรู้ทุกสิ่งที่อยู่กับฉัน! คุณฉลาดมาก! ขอบคุณมาก!
ตอนนี้งานเสร็จแล้วไปเล่นกันเถอะ!
[1]: มีวิธีหนึ่งที่จะโกงและเพิ่มทุกสิ่งจากที่หนึ่งไปยังอีกที่หนึ่งได้ในคราวเดียว เด็กบางคนชื่อเกาส์พบสิ่งนี้เมื่อเขาอายุแปดขวบ ฉันไม่ใช่คนฉลาด แต่อย่าถามฉันว่าเขาทำได้อย่างไร
ฉันมีวิธีที่ง่ายกว่าในการเข้าใจความซับซ้อนของเวลาที่เขาใช้กันทั่วไปในการคำนวณความซับซ้อนของเวลาคือสัญลักษณ์ Big O สิ่งนี้จะลบปัจจัยคงที่ทั้งหมดเพื่อให้เวลาในการรันสามารถประมาณโดยสัมพันธ์กับ N เมื่อ N เข้าใกล้อนันต์ โดยทั่วไปคุณสามารถคิดแบบนี้:
statement;
เป็นค่าคงที่ เวลาทำงานของคำสั่งจะไม่เปลี่ยนแปลงเมื่อเทียบกับ N
for ( i = 0; i < N; i++ )
statement;
เป็นเส้นตรง เวลาทำงานของลูปเป็นสัดส่วนโดยตรงกับ N เมื่อ N เพิ่มขึ้นเป็นสองเท่าดังนั้นเวลาทำงาน
for ( i = 0; i < N; i++ )
{
for ( j = 0; j < N; j++ )
statement;
}
เป็นกำลังสอง เวลาทำงานของสองลูปเป็นสัดส่วนกับกำลังสองของ N เมื่อ N เพิ่มขึ้นเป็นสองเท่าเวลาทำงานเพิ่มขึ้น N * N
while ( low <= high )
{
mid = ( low + high ) / 2;
if ( target < list[mid] )
high = mid - 1;
else if ( target > list[mid] )
low = mid + 1;
else break;
}
เป็นลอการิทึม เวลาทำงานของอัลกอริทึมเป็นสัดส่วนกับจำนวนครั้งที่ N สามารถหารด้วย 2 นี่เป็นเพราะอัลกอริทึมจะแบ่งพื้นที่ทำงานเป็นครึ่งหนึ่งกับการวนซ้ำแต่ละครั้ง
void quicksort ( int list[], int left, int right )
{
int pivot = partition ( list, left, right );
quicksort ( list, left, pivot - 1 );
quicksort ( list, pivot + 1, right );
}
คือ N * log (N) เวลาทำงานประกอบด้วย N ลูป (วนซ้ำหรือเรียกซ้ำ) ซึ่งเป็นลอการิทึมดังนั้นอัลกอริทึมจึงเป็นการรวมกันของเชิงเส้นและลอการิทึม
โดยทั่วไปการทำบางสิ่งกับทุกรายการในหนึ่งมิตินั้นเป็นแบบเส้นตรงการทำบางสิ่งกับทุกรายการในสองมิตินั้นเป็นกำลังสองและการแบ่งพื้นที่ทำงานครึ่งคือลอการิทึม มีมาตรการ Big O อื่น ๆ เช่นลูกบาศก์, เลขชี้กำลังและรากที่สอง แต่ก็ไม่ได้ใกล้เคียงกัน สัญกรณ์บิ๊กโอถูกอธิบายว่าเป็น O () ซึ่งเป็นตัวชี้วัด อัลกอริทึม quicksort จะอธิบายเป็น O (N * log (N))
หมายเหตุ: สิ่งเหล่านี้ไม่ได้คำนึงถึงมาตรการที่ดีที่สุดค่าเฉลี่ยและเลวร้ายที่สุด แต่ละคนจะมีสัญลักษณ์ Big O ของตัวเอง โปรดทราบว่านี่เป็นคำอธิบายที่ง่ายมาก Big O เป็นเรื่องธรรมดา แต่ก็ซับซ้อนกว่าที่ฉันเคยแสดง นอกจากนี้ยังมีสัญลักษณ์อื่น ๆ เช่นโอเมก้าใหญ่โอตัวน้อยและทีต้าตัวใหญ่ คุณอาจจะไม่พบพวกเขานอกหลักสูตรการวิเคราะห์อัลกอริทึม
สมมติว่าคุณสั่ง Harry Potter: รวบรวมภาพยนตร์ 8 เรื่อง [Blu-ray] จาก Amazon แล้วดาวน์โหลดภาพยนตร์เรื่องเดียวกันออนไลน์ในเวลาเดียวกัน คุณต้องการทดสอบว่าวิธีใดเร็วกว่า การจัดส่งใช้เวลาเกือบหนึ่งวันในการมาถึงและการดาวน์โหลดเสร็จสมบูรณ์ประมาณ 30 นาทีก่อนหน้า ที่ดี! ดังนั้นมันจึงเป็นการแข่งขันที่รัดกุม
จะเป็นอย่างไรถ้าฉันสั่งภาพยนตร์บลูเรย์หลายเรื่องเช่น The Lord of the Rings, Twilight, The Dark Knight Trilogy เป็นต้นและดาวน์โหลดภาพยนตร์ทั้งหมดออนไลน์ในเวลาเดียวกัน เวลานี้การส่งมอบยังคงใช้เวลาหนึ่งวันจึงจะเสร็จสมบูรณ์ แต่การดาวน์โหลดออนไลน์จะใช้เวลา 3 วันจึงจะเสร็จสิ้น สำหรับการซื้อสินค้าออนไลน์จำนวนรายการที่สั่งซื้อ (อินพุท) จะไม่มีผลกับเวลาการส่งมอบ ผลผลิตคงที่ เราเรียกสิ่งนี้O (1)
สำหรับการดาวน์โหลดออนไลน์เวลาดาวน์โหลดจะเป็นสัดส่วนโดยตรงกับขนาดไฟล์ภาพยนตร์ (อินพุต) เราเรียกสิ่งนี้O (n)
จากการทดลองเรารู้ว่าการช็อปปิ้งออนไลน์นั้นดีกว่าการดาวน์โหลดออนไลน์ มันสำคัญมากที่จะต้องเข้าใจสัญกรณ์ O ขนาดใหญ่เพราะมันช่วยให้คุณวิเคราะห์ความสามารถในการขยายและประสิทธิภาพของอัลกอริทึม
หมายเหตุ:สัญกรณ์ Big O แสดงถึงสถานการณ์ที่เลวร้ายที่สุดของอัลกอริทึม สมมติว่าO (1)และO (n)เป็นกรณีที่เลวร้ายที่สุดของตัวอย่างข้างต้น
การอ้างอิง : http://carlcheo.com/compsci
สมมติว่าเรากำลังพูดถึงขั้นตอนวิธีการซึ่งควรจะทำอะไรกับชุดข้อมูลที่มีขนาดn
จากนั้นO( <some expression X involving n> )
หมายความว่าในภาษาอังกฤษง่าย ๆ :
หากคุณโชคไม่ดีเมื่อดำเนินการ A อาจต้องใช้การดำเนินการ X (n) เท่าที่จะทำได้
มันเกิดขึ้นมีฟังก์ชั่นบางอย่าง (คิดว่ามันเป็นการนำไปใช้ของX (n) ) ที่มีแนวโน้มที่จะเกิดขึ้นค่อนข้างบ่อย เหล่านี้เป็นที่รู้จักกันดีและเปรียบเทียบได้อย่างง่ายดาย (ตัวอย่าง: 1
, Log N
, N
, N^2
, N!
ฯลฯ .. )
โดยการเปรียบเทียบสิ่งเหล่านี้เมื่อพูดถึงAและอัลกอริธึมอื่น ๆ มันเป็นเรื่องง่ายที่จะจัดอันดับอัลกอริธึมตามจำนวนการปฏิบัติการที่พวกเขาอาจจำเป็นต้องทำ
โดยทั่วไปเป้าหมายของเราคือการค้นหาหรือจัดโครงสร้างอัลกอริทึมAในแบบที่มันจะมีฟังก์ชั่นX(n)
ที่ให้ผลตอบแทนต่ำที่สุดเท่าที่จะเป็นไปได้
หากคุณมีความคิดเกี่ยวกับอินฟินิตี้ที่เหมาะสมในหัวของคุณแล้วมีคำอธิบายสั้น ๆ :
สัญลักษณ์ Big O บอกให้คุณทราบถึงค่าใช้จ่ายในการแก้ปัญหาที่มีขนาดใหญ่อย่างไม่ จำกัด
และยิ่งกว่านั้น
ปัจจัยคงที่นั้นเล็กน้อย
หากคุณอัปเกรดเป็นคอมพิวเตอร์ที่สามารถเรียกใช้อัลกอริทึมของคุณสองครั้งเร็วสัญกรณ์ O ขนาดใหญ่จะไม่สังเกตเห็นว่า การปรับปรุงปัจจัยอย่างต่อเนื่องนั้นเล็กเกินไปที่จะสังเกตได้แม้ในระดับที่สัญลักษณ์ O ขนาดใหญ่ใช้งานได้ โปรดทราบว่านี่เป็นส่วนหนึ่งโดยเจตนาของการออกแบบสัญกรณ์ O ขนาดใหญ่
แม้ว่าสิ่งใด "ใหญ่" กว่าปัจจัยคงที่สามารถตรวจจับได้อย่างไรก็ตาม
เมื่อสนใจที่จะทำการคำนวณที่มีขนาดใหญ่พอที่จะถือว่าเป็นอินฟินิตี้โดยประมาณแล้วสัญกรณ์ O ใหญ่จะประมาณค่าใช้จ่ายในการแก้ปัญหาของคุณ
หากสิ่งที่กล่าวมาข้างต้นไม่สมเหตุสมผลคุณก็ไม่มีความคิดเรื่องอินฟินิตี้ที่เข้าใจได้ง่ายในหัวของคุณและคุณอาจไม่สนใจทั้งหมดข้างต้น วิธีเดียวที่ฉันรู้ที่จะทำให้ความคิดเหล่านี้เข้มงวดหรืออธิบายให้พวกเขาหากพวกเขาไม่ได้มีประโยชน์อย่างสังหรณ์ใจคือการสอนสัญกรณ์ O ใหญ่หรือสิ่งที่คล้ายกัน (แม้ว่าเมื่อคุณเข้าใจสัญกรณ์ O ที่ยิ่งใหญ่ในอนาคตมันอาจจะคุ้มค่าที่จะทบทวนแนวคิดเหล่านี้อีกครั้ง)
คำอธิบายภาษาอังกฤษธรรมดาของสัญลักษณ์“ บิ๊กโอ” คืออะไร?
หมายเหตุด่วนมาก:
O ใน "Big O" หมายถึง "คำสั่งซื้อ" (หรือ "คำสั่งของ" อย่างแม่นยำ)
เพื่อให้คุณได้รับแนวคิดของมันอย่างแท้จริงว่ามันใช้เพื่อจัดเรียงบางสิ่งเพื่อเปรียบเทียบ
"Big O" ทำสองสิ่ง:
Notations
ประสบความสำเร็จข้างต้นทั้งสองที่มีมาตรฐานมีเจ็ดสัญลักษณ์ที่ใช้มากที่สุด
1
ขั้นตอนมันยอดเยี่ยมลำดับที่ 1logN
ตามขั้นตอนได้ดีลำดับที่ 2N
ขั้นตอนยุติธรรมลำดับที่ 3O(NlogN)
ขั้นตอนไม่ดีลำดับ 4N^2
ตามขั้นตอนไม่ดีลำดับที่ 52^N
ตามขั้นตอนมันช่างน่ากลัวลำดับ 6N!
ตามขั้นตอนมันแย่มากลำดับที่ 7สมมติว่าคุณได้สัญกรณ์O(N^2)
ไม่ใช่แค่คุณชัดเจนว่าวิธีนี้ใช้ขั้นตอน N * N เพื่อทำงานให้สำเร็จและคุณเห็นว่ามันไม่ดีเท่าO(NlogN)
การจัดอันดับ
โปรดทราบคำสั่งซื้อที่ปลายบรรทัดเพียงเพื่อความเข้าใจที่ดีขึ้นของคุณมีมากกว่า 7 สัญกรณ์หากพิจารณาความเป็นไปได้ทั้งหมด
ใน CS ชุดของขั้นตอนในการทำงานให้สำเร็จเรียกว่าอัลกอริทึม
ในคำศัพท์จะใช้สัญลักษณ์ Big O เพื่ออธิบายประสิทธิภาพหรือความซับซ้อนของอัลกอริทึม
นอกจากนี้ Big O ยังกำหนดตัวพิมพ์ที่แย่ที่สุดหรือวัดขั้นตอนขอบเขตบน
คุณสามารถอ้างถึง Big-Ω (Big-Omega) สำหรับกรณีที่ดีที่สุด
สัญกรณ์ Big-Ω (Big-Omega) (บทความ) | Khan Academy
สรุป
"Big O" อธิบายประสิทธิภาพของอัลกอริทึมและประเมินผล
หรือที่อยู่อย่างเป็นทางการ "บิ๊กโอ" จำแนกอัลกอริธึมและทำให้กระบวนการเปรียบเทียบเป็นมาตรฐาน
วิธีที่ง่ายที่สุดในการดู (ภาษาอังกฤษล้วน)
เรากำลังพยายามดูว่าจำนวนพารามิเตอร์อินพุตมีผลต่อเวลาทำงานของอัลกอริทึมอย่างไร หากเวลาทำงานของแอ็พพลิเคชันของคุณเป็นสัดส่วนกับจำนวนพารามิเตอร์อินพุตดังนั้นจะถูกกล่าวถึงใน Big O of n
ข้อความข้างต้นเป็นการเริ่มต้นที่ดี แต่ไม่เป็นความจริงอย่างสมบูรณ์
คำอธิบายที่แม่นยำยิ่งขึ้น (คณิตศาสตร์)
สมมติ
n = จำนวนพารามิเตอร์อินพุต
T (n) = ฟังก์ชั่นจริงที่แสดงเวลาทำงานของอัลกอริทึมเป็นฟังก์ชั่นของ n
c = ค่าคงที่
f (n) = ฟังก์ชั่นโดยประมาณที่แสดงเวลาทำงานของอัลกอริทึมเป็นฟังก์ชั่นของ n
จากนั้นตราบใดที่เกี่ยวข้องกับ Big O การประมาณ f (n) ถือว่าดีพอตราบใดที่เงื่อนไขด้านล่างเป็นจริง
lim T(n) ≤ c×f(n)
n→∞
สมการจะถูกอ่านในขณะที่ n เข้าใกล้อนันต์, T of n, น้อยกว่าหรือเท่ากับ c คูณ f ของ n
ในสัญกรณ์ O ขนาดใหญ่สิ่งนี้ถูกเขียนเป็น
T(n)∈O(n)
สิ่งนี้ถูกอ่านเมื่อ T of n อยู่ใน O ใหญ่ของ n
กลับไปเป็นภาษาอังกฤษ
ตามนิยามทางคณิตศาสตร์ข้างต้นหากคุณพูดว่าอัลกอริทึมของคุณคือ Big O ของ n หมายความว่ามันเป็นฟังก์ชันของ n (จำนวนพารามิเตอร์อินพุต) หรือเร็วกว่าหรือเร็วกว่าหากอัลกอริทึมของคุณคือ Big O ของ n มันก็จะเป็น Big O ของ n โดยอัตโนมัติ
Big O of n หมายความว่าอัลกอริทึมของฉันทำงานได้เร็วอย่างนี้ คุณไม่สามารถดูเครื่องหมาย Big O ของอัลกอริทึมของคุณและพูดช้า คุณสามารถพูดได้อย่างรวดเร็วเท่านั้น
ตรวจสอบนี้ออกสำหรับวิดีโอกวดวิชาที่ Big O จาก UC Berkley จริงๆแล้วมันเป็นแนวคิดที่เรียบง่าย ถ้าคุณได้ยินศาสตราจารย์ Shewchuck (อาจารย์ของพระเจ้าระดับเทพ) อธิบายมันคุณจะพูดว่า "โอ้นั่นคือทั้งหมดที่มันเป็น!"
ฉันพบคำอธิบายที่ดีมากเกี่ยวกับสัญกรณ์ O ที่ยิ่งใหญ่โดยเฉพาะอย่างยิ่งสำหรับคนที่ไม่ค่อยมีคณิตศาสตร์
https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/
สัญกรณ์ Big O ใช้ในวิทยาการคอมพิวเตอร์เพื่ออธิบายประสิทธิภาพหรือความซับซ้อนของอัลกอริทึม Big O อธิบายสถานการณ์กรณีที่เลวร้ายที่สุดโดยเฉพาะและสามารถใช้เพื่ออธิบายเวลาดำเนินการที่ต้องการหรือพื้นที่ที่ใช้ (เช่นในหน่วยความจำหรือดิสก์) โดยอัลกอริทึม
ทุกคนที่อ่านการเขียนโปรแกรม Pearls หรือหนังสือวิทยาศาสตร์คอมพิวเตอร์อื่น ๆ และไม่มีพื้นฐานในวิชาคณิตศาสตร์จะตีกำแพงเมื่อพวกเขามาถึงบทที่กล่าวถึง O (N log N) หรือไวยากรณ์ที่ดูบ้าคลั่งอื่น ๆ หวังว่าบทความนี้จะช่วยให้คุณเข้าใจพื้นฐานของ Big O และลอการิทึม
ในฐานะโปรแกรมเมอร์คนแรกและนักคณิตศาสตร์คนที่สอง (หรืออาจสามหรือสี่) ฉันพบวิธีที่ดีที่สุดในการเข้าใจ Big O อย่างละเอียดคือการสร้างตัวอย่างในโค้ด ดังนั้นด้านล่างคือคำสั่งทั่วไปของการเติบโตพร้อมคำอธิบายและตัวอย่างที่เป็นไปได้
O (1)
O (1) อธิบายอัลกอริทึมที่จะทำงานในเวลาเดียวกัน (หรือพื้นที่) เสมอโดยไม่คำนึงถึงขนาดของชุดข้อมูลอินพุต
bool IsFirstElementNull(IList<string> elements) { return elements[0] == null; }
บน)
O (N) อธิบายอัลกอริทึมที่ประสิทธิภาพจะเพิ่มขึ้นแบบเส้นตรงและเป็นสัดส่วนโดยตรงกับขนาดของชุดข้อมูลอินพุต ตัวอย่างด้านล่างยังแสดงให้เห็นว่า Big O สนับสนุนสถานการณ์จำลองประสิทธิภาพที่เลวร้ายที่สุดอย่างไร สตริงที่ตรงกันสามารถพบได้ในระหว่างการวนซ้ำใด ๆ ของ for loop และฟังก์ชั่นจะกลับมาเร็ว แต่สัญกรณ์ Big O จะถือว่าเป็นขีด จำกัด สูงสุดเสมอ
bool ContainsValue(IList<string> elements, string value) { foreach (var element in elements) { if (element == value) return true; } return false; }
O (N 2 )
O (N 2 ) หมายถึงอัลกอริทึมที่ประสิทธิภาพเป็นสัดส่วนโดยตรงกับกำลังสองของขนาดของชุดข้อมูลอินพุต นี่เป็นเรื่องปกติของอัลกอริธึมที่เกี่ยวข้องกับการวนซ้ำแบบซ้ำซ้อนกับชุดข้อมูล การวนซ้ำที่ลึกกว่าจะส่งผลให้ O (N 3 ), O (N 4 ) เป็นต้น
bool ContainsDuplicates(IList<string> elements) { for (var outer = 0; outer < elements.Count; outer++) { for (var inner = 0; inner < elements.Count; inner++) { // Don't compare with self if (outer == inner) continue; if (elements[outer] == elements[inner]) return true; } } return false; }
O (2 N )
O (2 N ) หมายถึงอัลกอริทึมที่การเติบโตเพิ่มขึ้นเป็นสองเท่าด้วยแต่ละส่วนเพิ่มในชุดข้อมูลอินพุต เส้นโค้งการเติบโตของฟังก์ชั่นO (2 N ) นั้นมีเอกซ์โพเนนเชียลโดยเริ่มจากจุดที่ตื้นมาก ตัวอย่างของฟังก์ชันO (2 N ) คือการคำนวณแบบเรียกซ้ำของหมายเลขฟีโบนักชี:
int Fibonacci(int number) { if (number <= 1) return number; return Fibonacci(number - 2) + Fibonacci(number - 1); }
ลอการิทึม
ลอการิทึมมีเล่ห์เหลี่ยมเล็กน้อยในการอธิบายดังนั้นฉันจะใช้ตัวอย่างทั่วไป:
การค้นหาแบบไบนารีเป็นเทคนิคที่ใช้ในการค้นหาชุดข้อมูลที่เรียงลำดับแล้ว มันทำงานได้โดยการเลือกองค์ประกอบกลางของชุดข้อมูลเป็นหลักมัธยฐานและเปรียบเทียบกับค่าเป้าหมาย หากค่าตรงกับมันจะกลับมาประสบความสำเร็จ หากค่าเป้าหมายสูงกว่าค่าขององค์ประกอบโพรบจะใช้เวลาครึ่งบนของชุดข้อมูลและดำเนินการเดียวกันกับมัน ในทำนองเดียวกันหากค่าเป้าหมายต่ำกว่าค่าขององค์ประกอบโพรบจะดำเนินการกับครึ่งล่าง มันจะยังคงลดลงครึ่งหนึ่งของชุดข้อมูลที่มีการทำซ้ำแต่ละครั้งจนกว่าจะพบค่าหรือจนกว่าจะไม่สามารถแยกชุดข้อมูล
อัลกอริทึมประเภทนี้อธิบายว่าเป็น O (บันทึก N) การแบ่งครึ่งของชุดข้อมูลซ้ำ ๆ ที่อธิบายไว้ในตัวอย่างการค้นหาแบบไบนารีจะสร้างเส้นโค้งการเติบโตที่จุดสูงสุดที่จุดเริ่มต้นและค่อยๆแบนออกเมื่อขนาดของชุดข้อมูลเพิ่มขึ้นเช่นชุดข้อมูลอินพุตที่มี 10 รายการใช้เวลาหนึ่งวินาที บรรจุ 100 รายการใช้เวลาสองวินาทีและชุดข้อมูลที่มี 1,000 รายการจะใช้เวลาสามวินาที การเพิ่มขนาดของชุดข้อมูลอินพุตเป็นสองเท่ามีผลเพียงเล็กน้อยต่อการเติบโตของมันเนื่องจากหลังจากการวนซ้ำของอัลกอริทึมชุดข้อมูลจะลดลงครึ่งหนึ่งดังนั้นจึงเท่ากับชุดข้อมูลอินพุตที่มีขนาดครึ่งหนึ่ง ทำให้อัลกอริทึมเช่นการค้นหาแบบไบนารีมีประสิทธิภาพอย่างมากเมื่อจัดการกับชุดข้อมูลขนาดใหญ่
นี่เป็นคำอธิบายที่ง่ายมาก แต่ฉันหวังว่ามันจะครอบคลุมรายละเอียดที่สำคัญที่สุด
สมมติว่าอัลกอริทึมของคุณจัดการกับปัญหานั้นขึ้นอยู่กับ 'ปัจจัย' บางอย่างเช่นทำให้มันเป็น N และ X
อัลกอริทึมของคุณจะต้องใช้การดำเนินการบางอย่างขึ้นอยู่กับ N และ X ตัวอย่างเช่นในกรณีที่แย่ที่สุดคือ3(N^2) + log(X)
การทำงาน
ตั้งแต่ Big-O ไม่ได้ดูแลมากเกินไปเกี่ยวกับปัจจัยคง (aka 3), บิ๊ก-O O(N^2 + log(X))
ของอัลกอริทึมของคุณ โดยทั่วไปแล้วมันแปลว่า 'ปริมาณการใช้งานที่อัลกอริทึมของคุณต้องการสำหรับกรณีที่แย่ที่สุดด้วย'
อัลกอริทึม : ขั้นตอน / สูตรสำหรับการแก้ปัญหา
วิเคราะห์อัลกอริธึมและเราจะเปรียบเทียบอัลกอริธึมได้อย่างไร
ตัวอย่าง:คุณและเพื่อนจะถูกขอให้สร้างฟังก์ชั่นเพื่อหาผลรวมของตัวเลขจาก 0 ถึง N คุณมาพร้อมกับ f (x) และเพื่อนของคุณมาพร้อมกับ g (x) ฟังก์ชันทั้งสองมีผลลัพธ์เหมือนกัน แต่เป็นอัลกอริธึมที่แตกต่างกัน เพื่อที่จะอคติเปรียบเทียบประสิทธิภาพของขั้นตอนวิธีที่เราใช้สัญกรณ์ Big-O
Big-O สัญกรณ์:อธิบายว่าการเติบโตของรันไทม์จะสัมพันธ์กับอินพุตอย่างรวดเร็วเพียงใดเมื่ออินพุทมีขนาดใหญ่ขึ้นโดยพลการ
3 ประเด็นสำคัญ:
ความซับซ้อนของพื้นที่:นอกเหนือจากความซับซ้อนของเวลาเรายังใส่ใจเกี่ยวกับความซับซ้อนของพื้นที่ (หน่วยความจำ / พื้นที่ที่อัลกอริทึมใช้) แทนที่จะตรวจสอบเวลาดำเนินการเราตรวจสอบขนาดของการจัดสรรหน่วยความจำ