คำอธิบายภาษาอังกฤษธรรมดาของสัญลักษณ์“ บิ๊กโอ” คืออะไร?


4934

ฉันต้องการคำจำกัดความที่เป็นทางการน้อยที่สุดเท่าที่จะทำได้


57
สรุป: ขอบเขตบนของความซับซ้อนของอัลกอริทึม ดูคำถามที่คล้ายกันบิ๊กโอคุณคำนวณ / ประเมินมันอย่างไร สำหรับการอธิบายที่ดี
Kosi2801

6
คำตอบอื่น ๆ ค่อนข้างดีมีเพียงรายละเอียดเดียวที่จะเข้าใจ: O (บันทึก n) หรือวิธีการที่คล้ายกันซึ่งขึ้นอยู่กับ "ความยาว" หรือ "ขนาด" ของอินพุตไม่ใช่ค่าตามตัวเอง อาจเข้าใจได้ยาก แต่มีความสำคัญมาก ตัวอย่างเช่นสิ่งนี้เกิดขึ้นเมื่ออัลกอริทึมของคุณแยกสองอย่างในแต่ละการวนซ้ำ
Harald Schilly

15
มีการบรรยายเกี่ยวกับความซับซ้อนของอัลกอริทึมในการบรรยาย 8 ของ MIT "หลักสูตรวิทยาศาสตร์คอมพิวเตอร์และการเขียนโปรแกรมเบื้องต้น" แน่นอนyoutube.com/watch?v=ewd7Lf2dr5Qไม่ใช่ภาษาอังกฤษที่สมบูรณ์ แต่ให้คำอธิบายที่ดีกับตัวอย่างที่เป็น เข้าใจง่าย
ivanjovanovic

17
Big O เป็นการประมาณค่าของประสิทธิภาพกรณีที่แย่ที่สุดของฟังก์ชันโดยสมมติว่าอัลกอริทึมจะทำการคำนวณซ้ำสูงสุด
พอล Sweatte

คำตอบ:


6626

หมายเหตุสั้น ๆ นี่เป็นสัญญลักษณ์ที่สับสนอย่างยิ่งของ Big O (ซึ่งเป็นขอบเขตบน) กับสัญกรณ์ Theta "Θ" (ซึ่งเป็นขอบเขตสองด้าน) จากประสบการณ์ของฉันนี่เป็นเรื่องปกติของการอภิปรายในการตั้งค่าที่ไม่ใช่เชิงวิชาการ ขออภัยในความสับสนที่เกิดขึ้น


ความซับซ้อนของ Big O สามารถมองเห็นได้ด้วยกราฟนี้:

การวิเคราะห์ 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 (แก้ไข)


578
ในขณะที่คำตอบอื่น ๆ ให้ความสำคัญกับการอธิบายความแตกต่างระหว่าง O (1), O (n ^ 2) และคณะ ... คุณเป็นคนที่ให้รายละเอียดว่าอัลกอริธึมสามารถจำแนกได้เป็น n ^ 2, nlog (n) ฯลฯ + 1 สำหรับคำตอบที่ดีที่ช่วยให้ฉันเข้าใจ Big O สัญกรณ์เช่นกัน
ยิวยาว

22
หนึ่งอาจต้องการเพิ่มว่า big-O หมายถึงขอบเขตบน (กำหนดโดยอัลกอริทึม) big-Omega ให้ขอบเขตที่ต่ำกว่า (มักจะได้รับการพิสูจน์อิสระจากอัลกอริทึมเฉพาะ) และ big-Theta หมายความว่าอัลกอริทึม "ดีที่สุด" ถึงขอบเขตที่เป็นที่รู้จักกัน
mdm

21
นี่เป็นสิ่งที่ดีหากคุณกำลังมองหาคำตอบที่ยาวที่สุด แต่ไม่ใช่คำตอบที่อธิบาย Big-O ได้ดีที่สุดในลักษณะที่เรียบง่าย
kirk.burleson

169
-1: ข้อผิดพลาดโจ๋งครึ่ม: _ "BigOh เป็นตัวแทนของความซับซ้อนของอัลกอริทึม" ไม่ BigOh เป็นขอบเขตบนแบบซีมโทติคและค่อนข้างมีอิสระจากวิทยาศาสตร์คอมพิวเตอร์ O (n) เป็นเส้นตรง เปล่าสิคุณสับสน BigOh กับทีต้า log n คือ O (n) 1 คือ O (n) จำนวน upvotes ของคำตอบนี้ (และความคิดเห็น) ซึ่งทำให้ความผิดพลาดพื้นฐานของการทำให้ Theta สับสนกับ BigOh ค่อนข้างน่าอาย ...

74
"เมื่อคุณเดินทางไปถึง 200 เมืองนั้นไม่มีเวลาเหลือพอในจักรวาลในการแก้ปัญหาด้วยคอมพิวเตอร์แบบดั้งเดิม" เมื่อจักรวาลกำลังจะจบลง?
Isaac

728

มันแสดงให้เห็นว่าอัลกอริทึมปรับขนาดตามขนาดอินพุต

O (n 2 ) : รู้จักกันในชื่อความซับซ้อนกำลังสอง

  • 1 รายการ: 1 วินาที
  • 10 รายการ: 100 วินาที
  • 100 รายการ: 10,000 วินาที

ขอให้สังเกตว่าจำนวนรายการที่เพิ่มขึ้นโดยปัจจัยที่ 10 แต่การเพิ่มขึ้นของเวลาโดยปัจจัยที่ 10 2 โดยทั่วไป n = 10 และอื่น ๆ O (n 2 ) ทำให้เรามีปัจจัยปรับ n 2ซึ่งเป็น 10 2

O (n) : รู้จักกันในชื่อLinear complex

  • 1 รายการ: 1 วินาที
  • 10 รายการ: 10 วินาที
  • 100 รายการ: 100 วินาที

เวลานี้จำนวนไอเท็มเพิ่มขึ้น 10 เท่าและเวลาก็เพิ่มขึ้น n = 10 และปัจจัยการปรับสเกลของ O (n) คือ 10

O (1) : เรียกว่าความซับซ้อนคงที่

  • 1 รายการ: 1 วินาที
  • 10 รายการ: 1 วินาที
  • 100 รายการ: 1 วินาที

จำนวนรายการยังคงเพิ่มขึ้น 10 เท่า แต่สัดส่วนการปรับของ O (1) เท่ากับ 1 เสมอ

O (บันทึก n) : รู้จักกันในชื่อความซับซ้อนลอการิทึม

  • 1 รายการ: 1 วินาที
  • 10 รายการ: 2 วินาที
  • 100 รายการ: 3 วินาที
  • 1,000 รายการ: 4 วินาที
  • 10,000 รายการ: 5 วินาที

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

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


5
คำจำกัดความนี้หมายความว่าอะไรกันแน่? (จำนวนรายการยังคงเพิ่มขึ้น 10 เท่า แต่ตัวคูณสเกลของ O (1) อยู่เสมอ 1)
Zach Smith

105
ไม่ใช่วินาทีการทำงาน นอกจากนี้คุณพลาดเวลาของแฟกทอเรียลและลอการิทึม
Chris Charabaruk

7
สิ่งนี้ไม่ได้อธิบายได้อย่างดีว่า O (n ^ 2) สามารถอธิบายอัลกอริทึมที่ทำงานอย่างแม่นยำ. 01 * n ^ 2 + 999999 * n + 999999 สิ่งสำคัญคือต้องรู้ว่าอัลกอริทึมนั้นถูกนำมาเปรียบเทียบกับมาตราส่วนนี้และ เปรียบเทียบทำงานเมื่อ n คือ 'ขนาดใหญ่พอ' Timsort ของ Python ใช้การเรียงลำดับการแทรก (แย่ที่สุด / ค่าเฉลี่ยกรณี O (n ^ 2)) สำหรับอาร์เรย์ขนาดเล็กเนื่องจากความจริงที่ว่ามันมีค่าใช้จ่ายเล็ก ๆ
Casey Kuball

6
คำตอบนี้ยังสร้างความสับสน O สัญกรณ์ใหญ่และสัญกรณ์ Theta ฟังก์ชั่นของ n ที่ส่งคืน 1 สำหรับอินพุตทั้งหมด (มักเขียนเป็น 1) เป็นจริงใน O (n ^ 2) (แม้ว่ามันจะยังอยู่ใน O (1)) ในทำนองเดียวกันอัลกอริทึมที่ต้องทำเพียงขั้นตอนเดียวซึ่งใช้เวลาเป็นจำนวนคงที่ถือว่าเป็นอัลกอริทึม O (1) แต่ยังเป็นอัลกอริทึม O (n) และ O (n ^ 2) แต่นักคณิตศาสตร์และนักวิทยาศาสตร์คอมพิวเตอร์อาจไม่เห็นด้วยกับคำจำกัดความ: - /
จาค็อบ Akkerboom

1
ความซับซ้อนของลอการิทึม O (log n) ที่พิจารณาในคำตอบนี้เป็นของฐาน 10 โดยทั่วไปมาตรฐานจะคำนวณด้วยฐาน 2 เราควรทราบข้อเท็จจริงนี้และไม่ควรสับสน นอกจากนี้ตามที่ระบุไว้โดย @ChrisCharabaruk ความซับซ้อนหมายถึงจำนวนของการดำเนินการและไม่ใช่วินาที
Aksh1801

405

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แต่สิ่งที่สำคัญคือ "เกล็ดเหมือน" )
  • ความน่าจะเป็นที่คาดหวังจำนวนคนที่ได้เห็นการตลาดแบบปากต่อปากเป็นหน้าที่ของเวลา
  • ระยะเวลาในการตอบสนองของเว็บไซต์ลดจำนวนหน่วยการประมวลผลใน CPU หรือ GPU หรือคลัสเตอร์คอมพิวเตอร์
  • ปริมาณความร้อนที่ส่งออกไปยังซีพียูนั้นตายเป็นหน้าที่ของจำนวนทรานซิสเตอร์แรงดันไฟฟ้าและอื่น ๆ
  • ระยะเวลาที่อัลกอริทึมจำเป็นต้องใช้งานเป็นฟังก์ชันของขนาดอินพุต
  • อัลกอริทึมจำเป็นต้องใช้เนื้อที่เท่าใดในการทำงานตามขนาดอินพุต

ตัวอย่าง

สำหรับตัวอย่างการจับมือกันดังกล่าวข้างต้นทุกคนอยู่ในมือทุกคนห้องพักสั่นอื่น ในตัวอย่าง#handshakes ∈ Ɵ(N²)นั้น ทำไม?

สำรองข้อมูลเล็กน้อย: จำนวนของการจับมือเป็นสิ่งที่ n-select-2 หรือN*(N-1)/2(คน N คนแต่ละคนจับมือของคนอื่น ๆ N-1 แต่การจับมือสองครั้งนี้นับด้วย 2):

ทุกคนจับมือกันทุกคน  เครดิตรูปภาพและใบอนุญาตตามบทความ Wikipedia / Wikimedia Commons "กราฟสมบูรณ์" เมทริกซ์คำคุณศัพท์

อย่างไรก็ตามสำหรับคนจำนวนมากเทอมเชิงเส้น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 ไม่ว่าปัจจัยคงที่คืออะไรถ้าฉันเป็นสองเท่าขนาดอินพุตเป็น ...

  • ... ฉันเพิ่มเวลาเป็นสองเท่าที่อัลกอริทึม O (N) ("linear time")

    N → (2N) = 2 ( N )

  • ... ฉันเพิ่มกำลังสองเป็นสองเท่า (สี่เท่า) เวลาที่อัลกอริทึม O (N²) ("กำลังสองกำลังสอง") (เช่นปัญหา 100x ที่ใหญ่ใช้100² = 10,000 เท่าตราบเท่าที่ ... อาจไม่ยั่งยืน)

    → (2N) ² = 4 ( )

  • ... ฉันสองคิวบ์ (octuple) เวลาที่อัลกอริทึม O (N³) ("ลูกบาศก์เวลา") ใช้ (เช่นปัญหา 100x เป็นใหญ่ใช้เวลา100³ = 1000000x ตราบเท่าที่ ... ไม่ยั่งยืนมาก)

    cN³ → c (2N) ³ = 8 ( cN³ )

  • ... ฉันเพิ่มจำนวนเงินที่แน่นอนให้กับเวลาที่อัลกอริทึม O (log (N)) ("เวลาลอการิทึม") " (ถูก!)

    c log (N) → c log (2N) = (c log (2)) + ( c log (N) ) = (จำนวนเงินคงที่) + ( c log (N) )

  • ... ฉันไม่เปลี่ยนเวลาที่อัลกอริทึม O (1) ("เวลาคงที่") " (ถูกที่สุด!)

    c * 1c * 1

  • ... ฉัน "(โดยทั่วไป) เป็นสองเท่า" เวลาที่อัลกอริทึม O (N log (N)) ใช้ " (ค่อนข้างธรรมดา)

    มันน้อยกว่า O (N 1.000001 ) ซึ่งคุณอาจยินดีที่จะโทรเป็นเส้นตรง

  • ... ฉันเพิ่มเวลาอย่างขันแข็งเพื่ออัลกอริทึมO (2 N ) ("เวลาเอ็กซ์โปเนนเชียล") (คุณจะเพิ่มเป็นสองเท่า (หรือสามเท่า)) เวลาเพียงเพิ่มปัญหาด้วยหน่วยเดียว)

    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 ไม่ดี (คอมพิวเตอร์สมัยใหม่สามารถทำงานได้หนึ่งพันล้านครั้งต่อวินาที)
  • หากเราใช้โครงสร้างแบบง่าย ๆ ที่เรียกว่าตารางแฮช (ตารางค้นหาแบบเร่งความเร็วทันทีหรือที่เรียกว่า hashmap หรือพจนานุกรม) เราต้องจ่ายค่าใช้จ่ายเพียงเล็กน้อยโดยดำเนินการทุกอย่างO(N)ตามเวลาที่กำหนดไว้ล่วงหน้า หลังจากนั้นก็ใช้เวลาโดยเฉลี่ยในการค้นหาบางสิ่งด้วยคีย์ของมันเท่านั้น (ในกรณีนี้คีย์ของเราคือพิกัดละติจูดและลองจิจูดปัดเศษเป็นกริดเราค้นหากริดสเปซที่อยู่ติดกันซึ่งมีเพียง 9 ซึ่งเป็น คงที่)
  • งานของเราเปลี่ยนจากสิ่งที่ไม่O(N²)สามารถจัดการได้O(N)และสิ่งที่เราต้องทำก็คือจ่ายค่าใช้จ่ายเล็กน้อยเพื่อทำตารางแฮช
  • คล้ายคลึง : การเปรียบเทียบในกรณีนี้คือตัวต่อ: เราสร้างโครงสร้างข้อมูลที่ใช้ประโยชน์จากคุณสมบัติของข้อมูล หากส่วนถนนของเราเหมือนชิ้นส่วนปริศนาเราจะจัดกลุ่มพวกมันด้วยการจับคู่สีและลวดลาย จากนั้นเราใช้ประโยชน์จากสิ่งนี้เพื่อหลีกเลี่ยงการทำงานพิเศษในภายหลัง (เปรียบเทียบชิ้นส่วนปริศนาที่มีสีเหมือนกันไม่ใช่ชิ้นส่วนปริศนาอื่น ๆ )

คุณธรรมของเรื่องราว: โครงสร้างข้อมูลช่วยให้เราเร่งดำเนินการได้เร็วขึ้น ยิ่งไปกว่านั้นโครงสร้างข้อมูลขั้นสูงยังช่วยให้คุณสามารถรวมล่าช้าหรือเพิกเฉยต่อการทำงานด้วยวิธีที่ชาญฉลาดอย่างเหลือเชื่อ ปัญหาที่แตกต่างกันจะมีการเปรียบเทียบที่แตกต่างกัน แต่พวกเขาทั้งหมดเกี่ยวข้องกับการจัดระเบียบข้อมูลในลักษณะที่ใช้ประโยชน์จากโครงสร้างบางอย่างที่เราสนใจหรือที่เราได้กำหนดไว้เพื่อทำบัญชี เราทำงานล่วงหน้า (โดยทั่วไปการวางแผนและการจัดระเบียบ) และตอนนี้งานที่ทำซ้ำได้ง่ายขึ้นมาก!


ตัวอย่างที่ใช้งานได้จริง: แสดงคำสั่งของการเติบโตในขณะที่เขียนโค้ด

สัญกรณ์เชิงซีโมติกนั้นอยู่ที่ใจกลางของมันค่อนข้างแยกจากการเขียนโปรแกรม สัญลักษณ์แบบอะซิมโทติคเป็นกรอบทางคณิตศาสตร์สำหรับการคิดเกี่ยวกับขนาดของสิ่งต่าง ๆ และสามารถใช้ในด้านต่าง ๆ ได้ ที่กล่าวว่า ... นี่คือวิธีการที่คุณใช้เครื่องหมายกำกับเชิงสัญลักษณ์กับการเข้ารหัส

พื้นฐาน: เมื่อใดก็ตามที่เราโต้ตอบกับทุกองค์ประกอบในคอลเลกชันขนาด A (เช่นอาร์เรย์ชุดคีย์ทั้งหมดของแผนที่ ฯลฯ ) หรือดำเนินการวนซ้ำของลูปซึ่งเป็นปัจจัยคูณของขนาด A ทำไมฉันถึงพูดว่า "a multiplicative factor"? - เนื่องจาก loops และ function (เกือบตามคำนิยาม) มีเวลาทำงานแบบ multiplicative: จำนวนการวนซ้ำ, เวลาที่ทำงานเสร็จในลูป (หรือสำหรับฟังก์ชัน: จำนวนครั้งที่คุณเรียก ฟังก์ชั่นเวลาทำงานเสร็จในฟังก์ชั่น) (สิ่งนี้จะเก็บไว้ถ้าเราไม่ทำอะไรแฟนซีเช่น skip ลูปหรือออกจากลูปก่อนหรือเปลี่ยนการควบคุมการไหลในฟังก์ชั่นตามข้อโต้แย้งซึ่งเป็นเรื่องธรรมดามาก) นี่เป็นตัวอย่างของเทคนิคการสร้างภาพด้วย pseudocode ประกอบ

(ที่นี่, xs แทนหน่วยเวลาคงที่, คำสั่งโปรเซสเซอร์, 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 ของอัลกอริทึมแบบขนาน

นอกจากนี้โปรดจำไว้ว่าเนื่องจากข้อ จำกัด ที่ซ่อนอยู่ในโปรแกรมของคุณคุณอาจไม่สนใจพฤติกรรมเชิงเส้นกำกับ คุณอาจทำงานกับค่าจำนวนที่ถูก จำกัด ตัวอย่างเช่น:

  • หากคุณกำลังจัดเรียงองค์ประกอบ 5 อย่างคุณไม่ต้องการใช้O(N log(N))quicksort ที่รวดเร็ว คุณต้องการใช้การเรียงลำดับการแทรกซึ่งเกิดขึ้นกับอินพุตขนาดเล็กได้ดี สถานการณ์เหล่านี้มักจะเกิดขึ้นในอัลกอริทึมการแบ่งและพิชิตที่คุณแบ่งปัญหาออกเป็นปัญหาย่อยที่เล็กลงและเล็กลงเช่นการเรียงซ้ำแบบซ้ำการแปลงฟูริเยร์ที่รวดเร็วหรือการคูณเมทริกซ์
  • หากค่าบางค่าถูก จำกัด ขอบเขตอย่างมีประสิทธิภาพเนื่องจากความเป็นจริงที่ซ่อนอยู่ (เช่นชื่อมนุษย์โดยเฉลี่ยมีขอบเขตเบา ๆ ที่ตัวอักษร 40 ตัวและอายุของมนุษย์นั้นมีขอบเขตที่เบา ๆ ที่ประมาณ 150) นอกจากนี้คุณยังสามารถกำหนดขอบเขตบนอินพุตของคุณเพื่อให้เงื่อนไขคงที่ได้อย่างมีประสิทธิภาพ

ในทางปฏิบัติแม้ในหมู่อัลกอริทึมที่มีประสิทธิภาพเชิงเดียวกันหรือคล้ายกันบุญญาติของพวกเขาอาจเป็นจริงได้แรงหนุนจากสิ่งอื่น ๆ เช่นปัจจัยด้านประสิทธิภาพอื่น ๆ (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)... และพิมพ์ได้ง่ายกว่า ;-)


28
คำตอบทางคณิตศาสตร์ที่ยอดเยี่ยม แต่ OP ได้ขอคำตอบภาษาอังกฤษธรรมดา ๆ คำอธิบายทางคณิตศาสตร์ระดับนี้ไม่จำเป็นต้องเข้าใจคำตอบ แต่สำหรับคนที่มีความคิดทางคณิตศาสตร์เป็นพิเศษมันอาจจะเข้าใจได้ง่ายกว่า "ภาษาอังกฤษธรรมดา" อย่างไรก็ตาม OP ขอให้หลัง
El Zorko

42
คนที่ไม่ใช่ OP อาจมีความสนใจในคำตอบของคำถามนี้ นั่นไม่ใช่หลักการชี้นำของไซต์ใช่ไหม
Casey

6
ในขณะที่ฉันอาจจะเห็นว่าทำไมผู้คนอาจอ่านคำตอบของฉันและคิดว่ามันเป็นคณิตศาสตร์เกินไป (โดยเฉพาะ "คำพูดที่เป็นภาษาอังกฤษธรรมดาใหม่" คำพูดเย้ยหยันตั้งแต่ลบ) คำถามเดิมถามเกี่ยวกับบิ๊กโอซึ่งเกี่ยวกับฟังก์ชั่นดังนั้นฉัน พยายามพูดให้ชัดเจนและพูดคุยเกี่ยวกับฟังก์ชั่นในลักษณะที่เติมเต็มสัญชาตญาณของภาษาอังกฤษ คณิตศาสตร์ที่นี่มักจะสามารถคัดสรรหรือเข้าใจกับพื้นหลังคณิตศาสตร์มัธยม ฉันรู้สึกว่าผู้คนอาจมอง Math Addenda ในตอนท้ายและคิดว่านั่นเป็นส่วนหนึ่งของคำตอบเมื่อมันอยู่ที่นั่นเพื่อดูว่าคณิตศาสตร์จริงมีลักษณะอย่างไร
ninjagecko

5
นี่คือคำตอบที่ยอดเยี่ยม; IMO ที่ดีกว่ามากด้วยคะแนนมากที่สุด "คณิตศาสตร์" ที่จำเป็นไม่ได้มีอะไรมากไปกว่าสิ่งที่จำเป็นในการทำความเข้าใจการแสดงออกในวงเล็บหลัง "O" ซึ่งไม่มีคำอธิบายที่สมเหตุสมผลที่ใช้ตัวอย่างใด ๆ ที่สามารถหลีกเลี่ยงได้
Dave Abrahams

1
"f (x) ∈ O (ส่วนบน) หมายถึง f" ไม่เติบโตเร็วกว่า "ส่วนบน" ทั้งสามคำพูดง่าย ๆ แต่คำอธิบายที่เหมาะสมทางคณิตศาสตร์ของโอ๋ใหญ่ธีตาและโอเมก้าเป็นสีทอง เขาอธิบายให้ฉันเป็นภาษาอังกฤษธรรมดาถึงจุดที่ 5 แหล่งข้อมูลที่แตกต่างกันไม่สามารถแปลให้ฉันได้โดยไม่ต้องเขียนนิพจน์ทางคณิตศาสตร์ที่ซับซ้อน ขอบคุณคน! :)
timbram

243

แก้ไข: บันทึกด่วนนี่เกือบจะแน่นอนทำให้เกิดสัญกรณ์ 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 จะเอาชนะอาร์เรย์ได้ในที่สุด


6
hashtable ต้องใช้อัลกอริทึมในการคำนวณดัชนีของอาร์เรย์ที่แท้จริง (ขึ้นอยู่กับการใช้งาน) และอาเรย์ก็มี O (1) เพราะมันเป็นแค่ที่อยู่ แต่สิ่งนี้ไม่มีอะไรเกี่ยวข้องกับคำถามเพียงแค่การสังเกต :)
46425 Philipp

7
คำอธิบายของจอนมีสิ่งที่ต้องทำอย่างมากกับคำถามที่ฉันคิด มันเป็นว่าวิธีการหนึ่งที่สามารถอธิบายให้แม่บางส่วนและในที่สุดเธอก็จะเข้าใจมัน :) ฉันคิดว่าฉันชอบตัวอย่างเสื้อผ้า (โดยเฉพาะอย่างยิ่งที่ผ่านมาซึ่งจะอธิบายถึงการเจริญเติบโตของความซับซ้อน)
โยฮันเน Schaub - litb

4
ภาษาฟิลิปปิ: ฉันไม่ได้พูดเกี่ยวกับที่อยู่อาร์เรย์โดยใช้ดัชนีฉันกำลังพูดถึงการค้นหารายการที่ตรงกันในอาร์เรย์ คุณสามารถอ่านคำตอบและดูว่ายังไม่ชัดเจนหรือไม่?
Jon Skeet

3
@Filip Ekberg ฉันคิดว่าคุณกำลังคิดตารางที่อยู่ตรงที่แต่ละดัชนีแมปไปยังคีย์โดยตรงจึงเป็น O (1) อย่างไรก็ตามฉันเชื่อว่า Jon กำลังพูดถึงคู่คีย์ / วาลที่ไม่เรียงลำดับซึ่งคุณต้องค้นหา ผ่านเชิงเส้น
ljs

2
@RBT: ไม่ไม่ใช่การค้นหาแบบไบนารี่ สามารถไปที่ถังแฮชที่ถูกต้องได้ทันทีโดยอิงตามการแปลงจากรหัสแฮชไปเป็นดัชนีที่ฝากข้อมูล หลังจากนั้นการค้นหารหัสแฮชที่ถูกต้องในที่เก็บข้อมูลอาจเป็นแบบเส้นตรงหรืออาจเป็นการค้นหาแบบไบนารี่ ... แต่ในเวลานั้นคุณจะมีสัดส่วนขนาดเล็กมากของขนาดรวมของพจนานุกรม
Jon Skeet

126

Big O อธิบายถึงขีด จำกัด สูงสุดของพฤติกรรมการเติบโตของฟังก์ชันตัวอย่างเช่นรันไทม์ของโปรแกรมเมื่ออินพุตมีขนาดใหญ่

ตัวอย่าง:

  • O (n): ถ้าฉันเพิ่มขนาดอินพุตเป็นสองเท่ารันไทม์จะเพิ่มเป็นสองเท่า

  • O (n 2 ): หากขนาดอินพุตเพิ่มขึ้นเป็นสองเท่าของรันไทม์สี่เท่ารันไทม์

  • O (บันทึก n): หากขนาดอินพุตเพิ่มขึ้นเป็นสองเท่ารันไทม์เพิ่มขึ้นหนึ่งรายการ

  • O (2 n ): หากขนาดอินพุตเพิ่มขึ้นหนึ่งรันไทม์จะเพิ่มเป็นสองเท่า

ขนาดอินพุตปกติพื้นที่ในบิตที่จำเป็นในการเป็นตัวแทนของอินพุต


7
! ไม่ถูกต้อง เช่น O (n): ถ้าฉันเพิ่มขนาดอินพุตเป็นสองเท่ารันไทม์จะคูณกับค่าคงที่ที่ไม่ใช่ศูนย์ ฉันหมายถึง O (n) = O (n + n)
arena-ru

7
ฉันกำลังพูดถึง f ใน f (n) = O (g (n)) ไม่ใช่ g อย่างที่คุณเข้าใจ
starblue

ฉัน upvoting แต่ประโยคสุดท้ายไม่ได้ช่วยอะไรมาก เรามักไม่พูดถึง "บิต" เมื่อพูดคุยหรือวัดขนาดใหญ่ (O)
cdiggins

5
คุณควรเพิ่มตัวอย่างสำหรับ O (n log n)
Christoffer Hammarström

นั่นไม่ชัดเจนดังนั้นโดยหลักแล้วมันจะทำงานแย่กว่า O (n) เล็กน้อย ดังนั้นหากไม่มีการเพิ่มขึ้นเป็นสองเท่ารันไทม์จะถูกคูณด้วยปัจจัยที่ค่อนข้างใหญ่กว่า 2
starblue

106

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

Big O มีประโยชน์ในการเปรียบเทียบว่าอัลกอริธึมจะขยายขนาดได้ดีเพียงใดเมื่อจำนวนอินพุตเพิ่มขึ้น

สัญกรณ์ Big O ที่แม่นยำยิ่งกว่านั้นใช้เพื่อแสดงพฤติกรรมเชิงซีมิโทติคของฟังก์ชั่น นั่นหมายความว่าฟังก์ชั่นนี้ทำงานเมื่อเข้าใกล้อนันต์

ในหลายกรณี "O" ของอัลกอริทึมจะตกอยู่ในกรณีใดกรณีหนึ่งต่อไปนี้:

  • O (1) - เวลาที่จะทำให้เหมือนกันโดยไม่คำนึงถึงขนาดของชุดอินพุต ตัวอย่างคือการเข้าถึงองค์ประกอบอาร์เรย์โดยดัชนี
  • O (Log N) - เวลาที่จะเสร็จสมบูรณ์จะเพิ่มขึ้นอย่างสอดคล้องกับ log2 (n) ตัวอย่างเช่น 1024 ไอเท็มใช้เวลาประมาณสองครั้งโดยมีความยาว 32 ไอเท็มเนื่องจาก Log2 (1024) = 10 และ Log2 (32) = 5 ตัวอย่างคือการค้นหาไอเท็มในแผนผังการค้นหาแบบไบนารี (BST)
  • O (N) - เวลาที่จะทำให้เสร็จสมบูรณ์ซึ่งปรับขนาดเชิงเส้นด้วยขนาดของชุดอินพุต กล่าวอีกนัยหนึ่งถ้าคุณเพิ่มจำนวนรายการในชุดอินพุตอัลกอริทึมจะใช้เวลานานเป็นสองเท่า ตัวอย่างคือการนับจำนวนรายการในรายการที่เชื่อมโยง
  • O (N Log N) - เวลาที่จะเพิ่มขึ้นให้เสร็จสมบูรณ์ตามจำนวนของรายการคูณผลลัพธ์ของ Log2 (N) ตัวอย่างนี้คือการจัดเรียงกองและการจัดเรียงอย่างรวดเร็ว
  • O (N ^ 2) - เวลาที่จะเสร็จสมบูรณ์เท่ากับคร่าวๆของจำนวนรายการ ตัวอย่างนี้คือการจัดเรียงฟอง
  • O (N!) - เวลาที่จะทำให้เสร็จสมบูรณ์คือแฟคทอเรียลของชุดอินพุต ตัวอย่างนี้เป็นวิธีการแก้ปัญหาพนักงานขายที่เดินทางแรงเดรัจฉาน

Big O จะไม่สนใจปัจจัยที่ไม่ได้มีส่วนช่วยในการสร้างกราฟเส้นโค้งการเติบโตของฟังก์ชั่นเมื่อขนาดอินพุตเพิ่มขึ้นต่ออินฟินิตี้ ซึ่งหมายความว่าค่าคงที่ที่เพิ่มหรือคูณด้วยฟังก์ชันจะถูกละเว้น


cdiggins จะเกิดอะไรขึ้นถ้าฉันมีความซับซ้อนของ O (N / 2) เช่น O (N) หรือ O (N / 2) ตัวอย่างเช่นอะไรที่ซับซ้อนถ้าฉันจะวนลูปมากกว่าครึ่งสตริง
Melad Basilius

1
@Melad นี่เป็นตัวอย่างของค่าคงที่ (0.5) ที่ถูกคูณกับฟังก์ชัน สิ่งนี้จะถูกละเว้นเนื่องจากถือว่ามีผลที่มีความหมายสำหรับค่าที่มีขนาดใหญ่มากของ N.
cdiggins

82

Big O เป็นเพียงวิธี "Express" ด้วยตัวคุณเองในแบบทั่วไป "ต้องใช้เวลา / เนื้อที่ในการรันโค้ดของฉันเท่าไหร่"

คุณมักจะเห็น O (n), O (n 2) ), O (nlogn) และอื่น ๆ ทั้งหมดนี้เป็นเพียงวิธีในการแสดง อัลกอริทึมเปลี่ยนแปลงอย่างไร

O (n) หมายถึง Big O คือ n และตอนนี้คุณอาจคิดว่า "What is n !?" "n" คือจำนวนขององค์ประกอบ การถ่ายภาพที่คุณต้องการค้นหารายการในอาร์เรย์ คุณจะต้องดูที่แต่ละองค์ประกอบและเป็น "คุณเป็นองค์ประกอบ / รายการที่ถูกต้องหรือไม่" ในกรณีที่เลวร้ายที่สุดรายการนั้นอยู่ที่ดัชนีสุดท้ายซึ่งหมายความว่าต้องใช้เวลามากพอ ๆ กับที่มีรายการอยู่ในรายการดังนั้นเราจึงควรพูดว่า "เฮ้เฮ้ n เป็นค่าที่ยุติธรรมพอสมควร!" .

ดังนั้นคุณอาจเข้าใจว่า "n 2 " " แต่จะเจาะจงมากขึ้นเล่นกับความคิดที่ว่าคุณมีขั้นตอนวิธีการเรียงลำดับที่ง่ายที่สุด การเรียงลำดับแบบฟอง อัลกอริทึมนี้จำเป็นต้องดูรายการทั้งหมดสำหรับแต่ละรายการ

รายการของฉัน

  1. 1
  2. 6
  3. 3

การไหลที่นี่จะเป็น:

  • เปรียบเทียบ 1 และ 6 ซึ่งใหญ่ที่สุด? Ok 6 อยู่ในตำแหน่งที่ถูกต้องแล้วเดินหน้าต่อไป!
  • เปรียบเทียบ 6 และ 3 โอ้น้อยกว่า 3! เรามาเริ่มกันเลยโอเคเปลี่ยนรายการเราต้องเริ่มจากจุดเริ่มต้นทันที!

นี่คือ O n 2เนื่องจากคุณต้องดูรายการทั้งหมดในรายการที่มีรายการ "n" สำหรับแต่ละรายการคุณดูรายการทั้งหมดอีกครั้งสำหรับการเปรียบเทียบนี่คือ "n" ดังนั้นสำหรับทุกรายการคุณดู "n" คูณความหมาย n * n = n 2

ฉันหวังว่านี่จะง่ายอย่างที่คุณต้องการ

แต่จำไว้ว่าบิ๊กโอเป็นเพียงวิธีหนึ่งที่จะช่วยให้คุณมีเวลาและสถานที่มากขึ้น


สำหรับ logN เราพิจารณาการวนลูปจาก 0 ถึง N / 2 แล้ว O (log log N) คืออะไร? ฉันหมายถึงโปรแกรมมีลักษณะอย่างไร ให้อภัยฉันสำหรับทักษะคณิตศาสตร์บริสุทธิ์
Govinda Sakhare

55

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 นับพันเท่าในกรณีนี้ทั้งหมดเนื่องจากวิธีที่อัลกอริธึมแต่ละตัว


40

นี่คือ 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 )


2
คุณช่วยพิจารณาการเปรียบเทียบที่แตกต่างกันสำหรับ O (1) ได้ไหม วิศวกรในฉันต้องการดึงการอภิปรายเกี่ยวกับความต้านทาน RF เนื่องจากสิ่งกีดขวาง
johnwbyrd

36

Big O เป็นการวัดจำนวนเวลา / พื้นที่ที่อัลกอริทึมใช้เทียบกับขนาดของอินพุต

หากอัลกอริทึมคือ O (n) เวลา / พื้นที่จะเพิ่มขึ้นในอัตราเดียวกับอินพุต

หากอัลกอริทึมเป็น O (n 2 ) ดังนั้นเวลา / พื้นที่จะเพิ่มขึ้นตามอัตราของอินพุตกำลังสอง

และอื่น ๆ


2
มันไม่เกี่ยวกับพื้นที่ มันเกี่ยวกับความซับซ้อนซึ่งหมายถึงเวลา
S.Lott

13
ฉันเชื่อเสมอว่ามันอาจเกี่ยวกับเวลาหรือสถานที่ แต่ไม่เกี่ยวกับทั้งสองในเวลาเดียวกัน
Rocco

9
ความซับซ้อนที่สุดแน่นอนเกี่ยวกับพื้นที่ ลองดูที่: en.wikipedia.org/wiki/PSPACE
Tom Crockett

4
คำตอบนี้เป็นคำว่า "ธรรมดา" ที่สุดที่นี่ คนก่อนหน้านี้ถือว่าผู้อ่านรู้พอที่จะเข้าใจ แต่ผู้เขียนไม่ได้ตระหนักถึงมัน พวกเขาคิดว่าพวกเขาเรียบง่ายและเรียบง่ายซึ่งไม่แน่นอน การเขียนข้อความจำนวนมากด้วยรูปแบบที่สวยงามและการสร้างตัวอย่างเทียมที่ยากต่อการไม่ใช่คน CS นั้นไม่ธรรมดาและเรียบง่ายมันเป็นเรื่องที่น่าดึงดูดใจสำหรับ stackoverflowers ที่ส่วนใหญ่เป็นคน CS เพื่อโหวต การอธิบายศัพท์ CS ในภาษาอังกฤษธรรมดาไม่ต้องการอะไรเกี่ยวกับรหัสและคณิตศาสตร์เลย +1 สำหรับคำตอบนี้แม้ว่ามันจะยังไม่ดีพอ
W.Sun

คำตอบนี้ทำให้เกิดข้อผิดพลาด (ทั่วไป) ของการสมมติว่า f = O (g) หมายความว่า f และ g เป็นสัดส่วน
Paul Hankin

33

คำอธิบายภาษาอังกฤษธรรมดาของ Big O คืออะไร ด้วยนิยามที่เป็นทางการเพียงเล็กน้อยเท่าที่จะเป็นไปได้และคณิตศาสตร์อย่างง่าย

คำอธิบายภาษาอังกฤษแบบธรรมดาเกี่ยวกับความต้องการสัญลักษณ์ Big-O:

เมื่อเราโปรแกรมเรากำลังพยายามแก้ปัญหา สิ่งที่เราเรียกว่าอัลกอริทึม สัญกรณ์ Big O ช่วยให้เราสามารถเปรียบเทียบประสิทธิภาพเคสที่แย่ลงของอัลกอริทึมของเราในแบบมาตรฐาน รายละเอียดฮาร์ดแวร์แตกต่างกันไปตามเวลาและการปรับปรุงในฮาร์ดแวร์สามารถลดเวลาที่ใช้อัลกอริทึมในการทำงาน แต่การเปลี่ยนฮาร์ดแวร์ไม่ได้หมายความว่าอัลกอริทึมของเราจะดีขึ้นหรือดีขึ้นเมื่อเวลาผ่านไปเนื่องจากอัลกอริทึมของเรายังคงเหมือนเดิม ดังนั้นเพื่อให้เราสามารถเปรียบเทียบอัลกอริทึมที่แตกต่างกันเพื่อตรวจสอบว่าดีกว่าหรือไม่เราใช้สัญกรณ์ Big O

คำอธิบายภาษาอังกฤษธรรมดาของสิ่งที่ Big O โน้ตคือ:

ขั้นตอนวิธีการทำงานไม่ได้ทั้งหมดในจำนวนเดียวกันของเวลาและสามารถแตกต่างกันไปขึ้นอยู่กับจำนวนของรายการในการป้อนข้อมูลซึ่งเราจะเรียกn จากนี้เราพิจารณาการวิเคราะห์กรณีที่แย่กว่านั้นหรือขอบเขตบนของเวลาทำงานเมื่อnมีขนาดใหญ่ขึ้นเรื่อย ๆ เราต้องระวังว่าnคืออะไรเพราะสัญลักษณ์บิ๊กโออ้างอิงหลายอย่าง


32

เป็นการยากที่จะวัดความเร็วของโปรแกรมซอฟต์แวร์และเมื่อเราลองคำตอบนั้นอาจซับซ้อนและเต็มไปด้วยข้อยกเว้นและกรณีพิเศษ นี่เป็นปัญหาใหญ่เนื่องจากข้อยกเว้นและกรณีพิเศษทั้งหมดนั้นทำให้เสียสมาธิและไม่ช่วยเหลือเมื่อเราต้องการเปรียบเทียบโปรแกรมที่แตกต่างกันสองรายการกับอีกรายการหนึ่งเพื่อค้นหาว่ารายการใดเป็น "เร็วที่สุด"

จากความซับซ้อนที่ไม่มีประโยชน์ทั้งหมดนี้ผู้คนพยายามอธิบายความเร็วของโปรแกรมซอฟต์แวร์โดยใช้นิพจน์ที่มีขนาดเล็กที่สุดและซับซ้อนน้อยที่สุดเท่าที่จะเป็นไปได้ การแสดงออกเหล่านี้เป็นการประมาณคร่าวๆอย่างมาก: ถึงแม้ว่าโชคดีนิดหน่อยพวกเขาจะบันทึก "แก่นแท้" ของว่าชิ้นส่วนของซอฟต์แวร์นั้นเร็วหรือช้า

เนื่องจากมันเป็นค่าประมาณเราจึงใช้ตัวอักษร "O" (Big Oh) ในการแสดงออกเพื่อเป็นการส่งสัญญาณไปยังผู้อ่านว่าเรากำลังสร้างการอนุมานขั้นต้น (และเพื่อให้แน่ใจว่าไม่มีใครผิดพลาดคิดว่าการแสดงออกนั้นถูกต้องในทางใดทางหนึ่ง)

หากคุณอ่านคำว่า "โอ้" ตามความหมาย "ตามลำดับ" หรือ "โดยประมาณ" คุณจะไม่ผิดไปมากนัก (ฉันคิดว่าการเลือกบิ๊กโอ้อาจเป็นเรื่องตลกได้)

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

ดี:

  • O(1) คงที่ : โปรแกรมใช้เวลาเดียวกันในการรันไม่ว่าอินพุตจะใหญ่แค่ไหน
  • O(log n) ลอการิทึม : รันไทม์ของโปรแกรมเพิ่มขึ้นอย่างช้าๆเท่านั้นถึงแม้จะมีขนาดใหญ่ขึ้นของอินพุต

ไม่ดี:

  • O(n) เป็น Linear : เวลาทำงานของโปรแกรมเพิ่มขึ้นตามขนาดของอินพุต
  • O(n^k) พหุนาม : - เวลาการประมวลผลเติบโตเร็วขึ้นและเร็วขึ้น - เป็นฟังก์ชันพหุนาม - เมื่อขนาดของอินพุตเพิ่มขึ้น

... และสิ่งที่น่าเกลียด:

  • O(k^n) เอ็กซ์โพเนนเชียลรันไทม์ของโปรแกรมเพิ่มขึ้นอย่างรวดเร็วโดยเพิ่มขนาดของปัญหาให้พอสมควร - เป็นประโยชน์เฉพาะกับการประมวลผลชุดข้อมูลขนาดเล็กที่มีอัลกอริธึมอธิบาย
  • O(n!) แฟกทอเรียลเวลาทำงานของโปรแกรมจะนานกว่าที่คุณจะสามารถรอได้ แต่ชุดข้อมูลที่มีขนาดเล็กที่สุดและดูธรรมดาที่สุด

4
ฉันยังเคยได้ยินคำว่า Linearithmic - O(n log n)ซึ่งก็ถือว่าดี
Jason Down

28

คำตอบที่ตรงไปตรงมาง่ายๆคือ:

Big O แสดงเวลา / พื้นที่ที่เลวร้ายที่สุดที่เป็นไปได้สำหรับอัลกอริทึมนั้น อัลกอริทึมจะไม่ใช้พื้นที่ / เวลามากกว่าที่กำหนดไว้ Big O แสดงถึงความซับซ้อนของเวลา / พื้นที่ในกรณีที่รุนแรง


28

ตกลง 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 )


3
while (size-->0)ฉันหวังว่าสิ่งนี้จะไม่ถามอีก
mr5

26

สัญลักษณ์ 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 เป็นค่าคงที่


เราสามารถใช้สัญกรณ์ BigO เพื่อวัดกรณีและกรณีที่แย่ที่สุดเช่นกัน en.wikipedia.org/wiki/Big_O_notation
cdiggins

25

" คำอธิบายภาษาอังกฤษธรรมดาของ Big O คืออะไรคำจำกัดความที่เป็นทางการที่สุดเท่าที่จะเป็นไปได้และคณิตศาสตร์ง่าย ๆ "

คำถามที่เรียบง่ายและสวยงามเช่นนั้นดูเหมือนว่าอย่างน้อยก็ควรได้รับคำตอบสั้น ๆ เช่นนักเรียนอาจได้รับในระหว่างการติว

สัญกรณ์ Big O เพียงบอกเวลาเท่าไร * อัลกอริทึมสามารถทำงานภายในได้ในแง่ของจำนวนข้อมูลอินพุตเท่านั้น **

(* ในความรู้สึกที่ยอดเยี่ยมของหน่วยเวลา!)
(** ซึ่งเป็นเรื่องสำคัญเพราะผู้คนมักจะต้องการมากขึ้นไม่ว่าพวกเขาจะมีชีวิตอยู่ในวันนี้หรือวันพรุ่งนี้)

ดีมากเกี่ยวกับสัญกรณ์ Big O ถ้านั่นคือสิ่งที่มันทำ?

  • จวนพูดวิเคราะห์ Big O คือมีประโยชน์และมีความสำคัญเพราะ Big O ทำให้โฟกัสเต็มที่ในขั้นตอนวิธีการของตัวเองความซับซ้อนและสมบูรณ์ละเว้นสิ่งที่เป็นเพียงสัดส่วนคงที่เหมือนเป็นเครื่องมือ JavaScript ความเร็วของ CPU ที่เชื่อมต่ออินเทอร์เน็ตของคุณและ ทุกสิ่งที่จะกลายเป็นอย่างรวดเร็วกลายเป็นล้าสมัยหาญเป็นรุ่นT Big O มุ่งเน้นไปที่การแสดงในวิธีที่มีความสำคัญเท่า ๆ กันกับผู้คนที่อาศัยอยู่ในปัจจุบันหรือในอนาคต

  • Big O สัญกรณ์ยังส่องสปอตไลโดยตรงบนหลักการที่สำคัญที่สุดของการเขียนโปรแกรมคอมพิวเตอร์ / วิศวกรรมความเป็นจริงที่เป็นแรงบันดาลใจเขียนโปรแกรมที่ดีที่จะทำให้ความคิดและความฝัน: วิธีเดียวที่จะบรรลุผลเกินเดือนมีนาคมไปข้างหน้าช้าของเทคโนโลยีคือการคิดค้นที่ดีกว่า ขั้นตอนวิธี


5
การถูกขอให้อธิบายบางสิ่งบางอย่างทางคณิตศาสตร์โดยไม่ใช้คณิตศาสตร์นั้นเป็นสิ่งที่ท้าทายสำหรับฉันเสมอ นักคณิตศาสตร์และอาจารย์ที่เชื่อว่าสิ่งนี้เป็นไปได้จริง และในฐานะที่เป็นโปรแกรมเมอร์ฉันหวังว่าไม่มีใครคิดว่าฉันตอบคำถามนี้โดยไม่ต้องใช้คณิตศาสตร์เพื่อเป็นความท้าทายที่ไม่อาจต้านทานได้อย่างสมบูรณ์
โจเซฟไมเออร์ส

22

ตัวอย่างอัลกอริทึม (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) ~ ทันที \ คงที่


2
ค่าคงที่ 4 ของคุณมาจากไหน
ร็อด

1
@Rod iterator init, การเปรียบเทียบตัววนซ้ำ, การอ่านตัววนซ้ำ, การเปรียบเทียบที่สำคัญ .. ฉันคิดว่าCน่าจะดีกว่า
Khaled.K

18

บิ๊กโอ

f (x) = O ( g (x)) เมื่อ x ไปที่ a (เช่น a = + ∞) หมายความว่ามีฟังก์ชันkเช่นนั้น:

  1. f (x) = k (x) g (x)

  2. 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 เช่นนั้น:

  1. f (x) = k (x) g (x)

  2. 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


3
คุณกำลังระบุ "Big O" และ "Small o" โดยไม่อธิบายสิ่งที่พวกเขาแนะนำแนวคิดทางคณิตศาสตร์จำนวนมากโดยไม่บอกว่าทำไมพวกเขาถึงมีความสำคัญและลิงก์ไปยังวิกิพีเดียอาจจะชัดเจนเกินไปสำหรับคำถามประเภทนี้
Adit Saxena

@AditSaxena คุณหมายถึงอะไร "โดยไม่ต้องอธิบายสิ่งที่พวกเขา"? ฉันอธิบายสิ่งที่พวกเขา นั่นคือ "big O" และ "small o" ไม่มีอะไรเลยเพียงสูตรเช่น "f (x) = O (g (x))" มีความหมายซึ่งฉันอธิบาย (เป็นภาษาอังกฤษธรรมดา แต่ไม่มีการกำหนด แน่นอนทุกสิ่งที่จำเป็นจากหลักสูตรแคลคูลัส) บางครั้ง "O (f (x))" ถูกมองว่าเป็นคลาส (จริง ๆ แล้วเป็นชุด) ของฟังก์ชั่นทั้งหมด "g (x)" เช่นนั้น "g (x) = O (f (x))" แต่นี่คือ ขั้นตอนพิเศษซึ่งไม่จำเป็นสำหรับการทำความเข้าใจพื้นฐาน
Alexey

เอาล่ะมีคำที่ไม่ใช่ภาษาอังกฤษธรรมดา ๆ แต่ก็หลีกเลี่ยงไม่ได้เว้นแต่ฉันจะต้องรวมคำจำกัดความที่จำเป็นทั้งหมดจากการวิเคราะห์ทางคณิตศาสตร์
Alexey

2
สวัสดี #Alexey โปรดดูคำตอบที่ยอมรับได้: มันยาว แต่มันถูกสร้างมาอย่างดีและมีรูปแบบที่ดี มันเริ่มต้นด้วยคำจำกัดความง่ายๆโดยไม่จำเป็นต้องมีพื้นฐานทางคณิตศาสตร์ ในขณะที่ทำเช่นนั้นเขาแนะนำคำ 3 "เทคนิค" ที่เขาอธิบายทันที (ญาติ, การเป็นตัวแทน, ความซับซ้อน) สิ่งนี้จะดำเนินไปทีละขั้นตอนในขณะที่ขุดลงในช่องนี้
Adit Saxena

2
Big O ใช้สำหรับการทำความเข้าใจพฤติกรรมเชิงซีมโทติคของอัลกอริทึมด้วยเหตุผลเดียวกันกับที่ใช้สำหรับการทำความเข้าใจพฤติกรรมแบบอะซิมโทติคของฟังก์ชั่น (พฤติกรรม asymptotic เป็นพฤติกรรมใกล้อินฟินิตี้) มันเป็นสัญกรณ์ที่สะดวกสบายสำหรับการเปรียบเทียบฟังก์ชั่นที่ซับซ้อน (เวลาจริงหรือพื้นที่ที่อัลกอริทึมใช้) กับสิ่งที่เรียบง่าย (อะไรที่เรียบง่ายมักจะเป็นฟังก์ชั่นพลังงาน) ใกล้กับอินฟินิตี้หรือใกล้สิ่งอื่นใด ฉันอธิบายว่ามันคืออะไร (ให้คำจำกัดความ) วิธีการคำนวณด้วย O ขนาดใหญ่เป็นเรื่องที่แตกต่างกันบางทีฉันจะเพิ่มตัวอย่างเนื่องจากคุณสนใจ
Alexey

18

สัญกรณ์ Big O เป็นวิธีการอธิบายว่าอัลกอริทึมจะทำงานได้เร็วเพียงใดโดยกำหนดจำนวนพารามิเตอร์อินพุตที่เราจะเรียกว่า "n" มันมีประโยชน์ในด้านวิทยาศาสตร์คอมพิวเตอร์เพราะเครื่องต่าง ๆ ทำงานด้วยความเร็วที่ต่างกันและเพียงบอกว่าอัลกอริทึมใช้เวลา 5 วินาทีไม่ได้บอกอะไรคุณมากนักในขณะที่คุณใช้ระบบที่มีโปรเซสเซอร์ Octo-Core 4.5 Ghz ฉันอาจใช้งานได้ ระบบ 15 800 Mhz อายุ 15 ปีซึ่งอาจใช้เวลานานกว่าโดยไม่คำนึงถึงอัลกอริทึม ดังนั้นแทนที่จะระบุว่าอัลกอริทึมทำงานเร็วแค่ไหนในแง่ของเวลาเราบอกว่ามันเร็วแค่ไหนในแง่ของจำนวนพารามิเตอร์อินพุตหรือ "n" ด้วยการอธิบายอัลกอริทึมด้วยวิธีนี้เราสามารถเปรียบเทียบความเร็วของอัลกอริทึมโดยไม่ต้องคำนึงถึงความเร็วของคอมพิวเตอร์เอง


11

ไม่แน่ใจว่าฉันมีส่วนร่วมในเรื่องนี้ต่อไป แต่ยังคิดว่าฉันจะแบ่งปัน: ฉันเคยพบโพสต์บล็อกนี้มีคำอธิบายและตัวอย่างที่เป็นประโยชน์ (แม้ว่าจะธรรมดามาก) และตัวอย่างใน Big O:

จากตัวอย่างนี้ช่วยให้เข้าใจพื้นฐานของกะโหลกศีรษะเหมือนกระดองเต่าของฉันดังนั้นฉันคิดว่าการอ่านโคตร 10 นาทีเพื่อให้คุณไปในทิศทางที่ถูกต้อง


@ วิลเลียม ... และผู้คนมีแนวโน้มที่จะตายจากวัยชราเผ่าพันธุ์สูญพันธุ์ดาวเคราะห์เปลี่ยนเป็นหมันและอื่น ๆ
Priidu Neemre

11

คุณต้องการที่จะรู้ว่าทุกสิ่งที่มีการรู้ของ 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]: มีวิธีหนึ่งที่จะโกงและเพิ่มทุกสิ่งจากที่หนึ่งไปยังอีกที่หนึ่งได้ในคราวเดียว เด็กบางคนชื่อเกาส์พบสิ่งนี้เมื่อเขาอายุแปดขวบ ฉันไม่ใช่คนฉลาด แต่อย่าถามฉันว่าเขาทำได้อย่างไร


9

ฉันมีวิธีที่ง่ายกว่าในการเข้าใจความซับซ้อนของเวลาที่เขาใช้กันทั่วไปในการคำนวณความซับซ้อนของเวลาคือสัญลักษณ์ 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 เป็นเรื่องธรรมดา แต่ก็ซับซ้อนกว่าที่ฉันเคยแสดง นอกจากนี้ยังมีสัญลักษณ์อื่น ๆ เช่นโอเมก้าใหญ่โอตัวน้อยและทีต้าตัวใหญ่ คุณอาจจะไม่พบพวกเขานอกหลักสูตรการวิเคราะห์อัลกอริทึม


9

สมมติว่าคุณสั่ง 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


9

สมมติว่าเรากำลังพูดถึงขั้นตอนวิธีการซึ่งควรจะทำอะไรกับชุดข้อมูลที่มีขนาดn

จากนั้นO( <some expression X involving n> )หมายความว่าในภาษาอังกฤษง่าย ๆ :

หากคุณโชคไม่ดีเมื่อดำเนินการ A อาจต้องใช้การดำเนินการ X (n) เท่าที่จะทำได้

มันเกิดขึ้นมีฟังก์ชั่นบางอย่าง (คิดว่ามันเป็นการนำไปใช้ของX (n) ) ที่มีแนวโน้มที่จะเกิดขึ้นค่อนข้างบ่อย เหล่านี้เป็นที่รู้จักกันดีและเปรียบเทียบได้อย่างง่ายดาย (ตัวอย่าง: 1, Log N, N, N^2, N!ฯลฯ .. )

โดยการเปรียบเทียบสิ่งเหล่านี้เมื่อพูดถึงAและอัลกอริธึมอื่น ๆ มันเป็นเรื่องง่ายที่จะจัดอันดับอัลกอริธึมตามจำนวนการปฏิบัติการที่พวกเขาอาจจำเป็นต้องทำ

โดยทั่วไปเป้าหมายของเราคือการค้นหาหรือจัดโครงสร้างอัลกอริทึมAในแบบที่มันจะมีฟังก์ชั่นX(n)ที่ให้ผลตอบแทนต่ำที่สุดเท่าที่จะเป็นไปได้


8

หากคุณมีความคิดเกี่ยวกับอินฟินิตี้ที่เหมาะสมในหัวของคุณแล้วมีคำอธิบายสั้น ๆ :

สัญลักษณ์ Big O บอกให้คุณทราบถึงค่าใช้จ่ายในการแก้ปัญหาที่มีขนาดใหญ่อย่างไม่ จำกัด

และยิ่งกว่านั้น

ปัจจัยคงที่นั้นเล็กน้อย

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

แม้ว่าสิ่งใด "ใหญ่" กว่าปัจจัยคงที่สามารถตรวจจับได้อย่างไรก็ตาม

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


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


7

คำอธิบายภาษาอังกฤษธรรมดาของสัญลักษณ์“ บิ๊กโอ” คืออะไร?

หมายเหตุด่วนมาก:

O ใน "Big O" หมายถึง "คำสั่งซื้อ" (หรือ "คำสั่งของ" อย่างแม่นยำ)
เพื่อให้คุณได้รับแนวคิดของมันอย่างแท้จริงว่ามันใช้เพื่อจัดเรียงบางสิ่งเพื่อเปรียบเทียบ

  • "Big O" ทำสองสิ่ง:

    1. ประมาณจำนวนขั้นตอนของวิธีการที่คอมพิวเตอร์ของคุณใช้ในการทำงานให้สำเร็จ
    2. ทำให้กระบวนการเปรียบเทียบกับผู้อื่นง่ายขึ้นเพื่อตัดสินว่ามันดีหรือไม่?
    3. "บิ๊ก O' Notationsประสบความสำเร็จข้างต้นทั้งสองที่มีมาตรฐาน
  • มีเจ็ดสัญลักษณ์ที่ใช้มากที่สุด

    1. O (1) หมายถึงคอมพิวเตอร์ของคุณทำงานได้ตาม1ขั้นตอนมันยอดเยี่ยมลำดับที่ 1
    2. O (logN) หมายถึงคอมพิวเตอร์ของคุณทำlogNตามขั้นตอนได้ดีลำดับที่ 2
    3. O (N) เสร็จงานด้วยNขั้นตอนยุติธรรมลำดับที่ 3
    4. O (NlogN) จบงานด้วยO(NlogN)ขั้นตอนไม่ดีลำดับ 4
    5. O (N ^ 2) ทำงานให้เสร็จN^2ตามขั้นตอนไม่ดีลำดับที่ 5
    6. O (2 ^ N) ทำงานให้เสร็จ2^Nตามขั้นตอนมันช่างน่ากลัวลำดับ 6
    7. O (N!) ทำงานให้เสร็จN!ตามขั้นตอนมันแย่มากลำดับที่ 7

ป้อนคำอธิบายรูปภาพที่นี่

สมมติว่าคุณได้สัญกรณ์O(N^2)ไม่ใช่แค่คุณชัดเจนว่าวิธีนี้ใช้ขั้นตอน N * N เพื่อทำงานให้สำเร็จและคุณเห็นว่ามันไม่ดีเท่าO(NlogN)การจัดอันดับ

โปรดทราบคำสั่งซื้อที่ปลายบรรทัดเพียงเพื่อความเข้าใจที่ดีขึ้นของคุณมีมากกว่า 7 สัญกรณ์หากพิจารณาความเป็นไปได้ทั้งหมด

ใน CS ชุดของขั้นตอนในการทำงานให้สำเร็จเรียกว่าอัลกอริทึม
ในคำศัพท์จะใช้สัญลักษณ์ Big O เพื่ออธิบายประสิทธิภาพหรือความซับซ้อนของอัลกอริทึม

นอกจากนี้ Big O ยังกำหนดตัวพิมพ์ที่แย่ที่สุดหรือวัดขั้นตอนขอบเขตบน
คุณสามารถอ้างถึง Big-Ω (Big-Omega) สำหรับกรณีที่ดีที่สุด

สัญกรณ์ Big-Ω (Big-Omega) (บทความ) | Khan Academy

  • สรุป
    "Big O" อธิบายประสิทธิภาพของอัลกอริทึมและประเมินผล

    หรือที่อยู่อย่างเป็นทางการ "บิ๊กโอ" จำแนกอัลกอริธึมและทำให้กระบวนการเปรียบเทียบเป็นมาตรฐาน


6

วิธีที่ง่ายที่สุดในการดู (ภาษาอังกฤษล้วน)

เรากำลังพยายามดูว่าจำนวนพารามิเตอร์อินพุตมีผลต่อเวลาทำงานของอัลกอริทึมอย่างไร หากเวลาทำงานของแอ็พพลิเคชันของคุณเป็นสัดส่วนกับจำนวนพารามิเตอร์อินพุตดังนั้นจะถูกกล่าวถึงใน 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 (อาจารย์ของพระเจ้าระดับเทพ) อธิบายมันคุณจะพูดว่า "โอ้นั่นคือทั้งหมดที่มันเป็น!"


5

ฉันพบคำอธิบายที่ดีมากเกี่ยวกับสัญกรณ์ 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 รายการจะใช้เวลาสามวินาที การเพิ่มขนาดของชุดข้อมูลอินพุตเป็นสองเท่ามีผลเพียงเล็กน้อยต่อการเติบโตของมันเนื่องจากหลังจากการวนซ้ำของอัลกอริทึมชุดข้อมูลจะลดลงครึ่งหนึ่งดังนั้นจึงเท่ากับชุดข้อมูลอินพุตที่มีขนาดครึ่งหนึ่ง ทำให้อัลกอริทึมเช่นการค้นหาแบบไบนารีมีประสิทธิภาพอย่างมากเมื่อจัดการกับชุดข้อมูลขนาดใหญ่


4

นี่เป็นคำอธิบายที่ง่ายมาก แต่ฉันหวังว่ามันจะครอบคลุมรายละเอียดที่สำคัญที่สุด

สมมติว่าอัลกอริทึมของคุณจัดการกับปัญหานั้นขึ้นอยู่กับ 'ปัจจัย' บางอย่างเช่นทำให้มันเป็น N และ X

อัลกอริทึมของคุณจะต้องใช้การดำเนินการบางอย่างขึ้นอยู่กับ N และ X ตัวอย่างเช่นในกรณีที่แย่ที่สุดคือ3(N^2) + log(X)การทำงาน

ตั้งแต่ Big-O ไม่ได้ดูแลมากเกินไปเกี่ยวกับปัจจัยคง (aka 3), บิ๊ก-O O(N^2 + log(X))ของอัลกอริทึมของคุณ โดยทั่วไปแล้วมันแปลว่า 'ปริมาณการใช้งานที่อัลกอริทึมของคุณต้องการสำหรับกรณีที่แย่ที่สุดด้วย'


4

คำนำ

อัลกอริทึม : ขั้นตอน / สูตรสำหรับการแก้ปัญหา


วิเคราะห์อัลกอริธึมและเราจะเปรียบเทียบอัลกอริธึมได้อย่างไร

ตัวอย่าง:คุณและเพื่อนจะถูกขอให้สร้างฟังก์ชั่นเพื่อหาผลรวมของตัวเลขจาก 0 ถึง N คุณมาพร้อมกับ f (x) และเพื่อนของคุณมาพร้อมกับ g (x) ฟังก์ชันทั้งสองมีผลลัพธ์เหมือนกัน แต่เป็นอัลกอริธึมที่แตกต่างกัน เพื่อที่จะอคติเปรียบเทียบประสิทธิภาพของขั้นตอนวิธีที่เราใช้สัญกรณ์ Big-O

Big-O สัญกรณ์:อธิบายว่าการเติบโตของรันไทม์จะสัมพันธ์กับอินพุตอย่างรวดเร็วเพียงใดเมื่ออินพุทมีขนาดใหญ่ขึ้นโดยพลการ

3 ประเด็นสำคัญ:

  1. เปรียบเทียบการเติบโตของรันไทม์ที่รวดเร็ว ไม่ เปรียบเทียบรันไทม์ที่แน่นอน (ขึ้นอยู่กับฮาร์ดแวร์)
  2. เฉพาะที่เกี่ยวข้องกับรันไทม์เติบโตสัมพันธ์กับอินพุต(n)
  3. ในขณะที่nมีขนาดใหญ่ตามอำเภอใจให้มุ่งไปที่คำศัพท์ที่จะเติบโตเร็วที่สุดเมื่อ n มีขนาดใหญ่ (คิดไม่สิ้นสุด) การวิเคราะห์เชิงเส้นกำกับของ AKA

ความซับซ้อนของพื้นที่:นอกเหนือจากความซับซ้อนของเวลาเรายังใส่ใจเกี่ยวกับความซับซ้อนของพื้นที่ (หน่วยความจำ / พื้นที่ที่อัลกอริทึมใช้) แทนที่จะตรวจสอบเวลาดำเนินการเราตรวจสอบขนาดของการจัดสรรหน่วยความจำ

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