อะไรจะทำให้อัลกอริทึมมีความซับซ้อน O (log log n)


106

คำถามก่อนหน้านี้กล่าวถึงปัจจัยบางประการที่อาจทำให้อัลกอริทึมมีความซับซ้อน O (log n)

อะไรจะทำให้อัลกอริทึมมีความซับซ้อนของเวลา O (log log n)?

คำตอบ:


219

คำศัพท์ O (log n) สามารถแสดงในที่ต่างๆได้ แต่โดยทั่วไปจะมีสองเส้นทางหลักที่จะมาถึงรันไทม์นี้

การหดตัวด้วยรากที่สอง

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

ที่น่าสนใจคือมีวิธีการที่คล้ายกันในการย่อขนาดของปัญหาที่ทำให้เกิด runtimes ของรูปแบบ O (log log n) แทนที่จะแบ่งครึ่งอินพุตในแต่ละเลเยอร์จะเกิดอะไรขึ้นถ้าเราหารากที่สองของขนาดในแต่ละเลเยอร์

ตัวอย่างเช่นลองหาเลข 65,536 เราต้องหารมันด้วย 2 กี่ครั้งจนกว่าเราจะได้ 1? ถ้าเราทำเช่นนี้เราจะได้รับ

  • 65,536 / 2 = 32,768
  • 32,768 / 2 = 16,384
  • 16,384 / 2 = 8,192
  • 8,192 / 2 = 4,096
  • 4,096 / 2 = 2,048
  • 2,048 / 2 = 1,024
  • 1,024 / 2 = 512
  • 512/2 = 256
  • 256/2 = 128
  • 128/2 = 64
  • 64/2 = 32
  • 32/2 = 16
  • 16/2 = 8
  • 8/2 = 4
  • 4/2 = 2
  • 2/2 = 1

กระบวนการนี้ใช้เวลา 16 ขั้นตอนและมันยังเป็นกรณีที่ 65,536 = 2 16

แต่ถ้าเราหารากที่สองในแต่ละระดับเราจะได้

  • √65,536 = 256
  • √256 = 16
  • √16 = 4
  • √4 = 2

สังเกตว่าใช้เวลาเพียงสี่ขั้นตอนในการลงไปจนถึง 2 ทำไมจึงเป็นเช่นนี้

ประการแรกคำอธิบายที่เข้าใจง่าย n และ√nมีกี่หลัก? มีจำนวนประมาณ n หลักในจำนวน n และประมาณ log (√n) = log (n 1/2 ) = (1/2) log n หลักใน√n ซึ่งหมายความว่าทุกครั้งที่คุณหารากที่สองคุณจะลดจำนวนหลักลงครึ่งหนึ่งโดยประมาณ เนื่องจากคุณสามารถลดปริมาณ k O (log k) ลงครึ่งหนึ่งเท่านั้นก่อนที่จะลดลงเป็นค่าคงที่ (พูด 2) ซึ่งหมายความว่าคุณสามารถใช้รากที่สอง O (log log n) เท่าก่อนที่คุณจะลดจำนวนลง เป็นค่าคงที่ (พูด 2)

ตอนนี้เรามาทำคณิตศาสตร์เพื่อทำให้มันเข้มงวด ไม่ได้เขียนลำดับข้างต้นใหม่ในแง่ของพลังของสอง:

  • √65,536 = √2 16 = (2 16 ) 1/2 = 2 8 = 256
  • √256 = √2 8 = (2 8 ) 1/2 = 2 4 = 16
  • √16 = √2 4 = (2 4 ) 1/2 = 2 2 = 4
  • √4 = √2 2 = (2 2 ) 1/2 = 2 1 = 2

สังเกตว่าเราทำตามลำดับ 2 16 → 2 8 → 2 4 → 2 2 → 2 1 ในการวนซ้ำแต่ละครั้งเราตัดเลขชี้กำลังของกำลังสองลงครึ่งหนึ่ง มันน่าสนใจเพราะสิ่งนี้เชื่อมโยงกลับไปยังสิ่งที่เรารู้อยู่แล้ว - คุณหารจำนวน k ได้ครึ่งหนึ่ง O (log k) เท่าก่อนที่มันจะลดลงเหลือศูนย์

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

ตัวอย่างหนึ่งในโลกแห่งความเป็นจริงคือต้นไม้ van Emde Boas(vEB-tree) โครงสร้างข้อมูล vEB-tree เป็นโครงสร้างข้อมูลเฉพาะสำหรับการจัดเก็บจำนวนเต็มในช่วง 0 ... N - 1 ทำงานดังนี้โหนดรากของทรีมีพอยน์เตอร์√Nแยกช่วง 0 ... N - 1 ใน√Nที่เก็บข้อมูลแต่ละช่วงมีจำนวนเต็มประมาณ√N จากนั้นที่เก็บข้อมูลเหล่านี้จะถูกแบ่งย่อยภายในเป็น√ (√ N) ที่เก็บข้อมูลซึ่งแต่ละส่วนมีองค์ประกอบ√ (√ N) โดยประมาณ ในการสำรวจต้นไม้คุณเริ่มต้นที่รากกำหนดถังที่คุณเป็นสมาชิกจากนั้นดำเนินการต่อซ้ำในทรีย่อยที่เหมาะสม เนื่องจากวิธีการจัดโครงสร้าง vEB-tree คุณสามารถกำหนดได้ใน O (1) เวลาที่ทรีย่อยจะลงมาและหลังจากขั้นตอน O (บันทึกบันทึก N) คุณจะไปถึงด้านล่างของทรี ดังนั้นการค้นหาใน vEB-tree ใช้เวลาเพียง O (log log N)

อีกตัวอย่างหนึ่งคือHopcroft-ฟอร์จูนคู่ใกล้เคียงที่สุดของขั้นตอนวิธีการจุด อัลกอริทึมนี้พยายามค้นหาจุดที่ใกล้เคียงที่สุดสองจุดในชุดของจุด 2 มิติ ทำงานโดยการสร้างตารางของถังและกระจายคะแนนลงในถังเหล่านั้น หากพบที่เก็บข้อมูลที่จุดใดจุดหนึ่งในอัลกอริทึมที่มีจุดมากกว่า√Nอัลกอริทึมจะประมวลผลที่เก็บข้อมูลนั้นซ้ำ ดังนั้นความลึกสูงสุดของการเรียกซ้ำจึงเป็น O (log log n) และเมื่อใช้การวิเคราะห์โครงสร้างการเรียกซ้ำจะแสดงให้เห็นว่าแต่ละเลเยอร์ในต้นไม้ทำงาน O (n) ดังนั้นรันไทม์ทั้งหมดของอัลกอริทึมคือ O (n log log n)

O (log n) อัลกอริทึมบนอินพุตขนาดเล็ก

มีอัลกอริทึมอื่น ๆ ที่บรรลุ O (log log n) runtimes โดยใช้อัลกอริทึมเช่นการค้นหาแบบไบนารีบนวัตถุขนาด O (log n) ตัวอย่างเช่นโครงสร้างข้อมูลx-fast trieทำการค้นหาแบบไบนารีบนเลเยอร์ของต้นไม้ที่มีความสูง O (log U) ดังนั้นรันไทม์สำหรับการดำเนินการบางอย่างจึงเป็น O (log log U) trie y-fastที่เกี่ยวข้องได้รับรันไทม์ O (log log U) บางส่วนโดยการรักษา BSTs ที่สมดุลของโหนด O (log U) แต่ละโหนดทำให้การค้นหาในต้นไม้เหล่านั้นทำงานในเวลา O (log log U) ต้นไม้เต้นจังหวะแทงโก้และที่เกี่ยวข้องต้นไม้ multisplayโครงสร้างข้อมูลท้ายด้วย O (บันทึกบันทึก n) ระยะในการวิเคราะห์ของพวกเขาเพราะพวกเขารักษาต้นไม้ที่มี O (log n) แต่ละรายการ

ตัวอย่างอื่น ๆ

อัลกอริทึมอื่น ๆ บรรลุรันไทม์ O (log log n) ด้วยวิธีอื่น Interpolation searchคาดว่ารันไทม์ O (log log n) เพื่อค้นหาตัวเลขในอาร์เรย์ที่เรียงลำดับ แต่การวิเคราะห์มีส่วนเกี่ยวข้องพอสมควร ในท้ายที่สุดการวิเคราะห์จะทำงานโดยแสดงให้เห็นว่าจำนวนการทำซ้ำเท่ากับจำนวน k ดังกล่าวซึ่ง n 2 -k ≤ 2 ซึ่งบันทึกบันทึก n เป็นวิธีแก้ปัญหาที่ถูกต้อง อัลกอริทึมบางอย่างเช่นอัลกอริทึมCheriton-Tarjan MSTมาถึงรันไทม์ที่เกี่ยวข้องกับ O (log log n) โดยการแก้ปัญหาการเพิ่มประสิทธิภาพที่มีข้อ จำกัด ที่ซับซ้อน

หวังว่านี่จะช่วยได้!


5
@ JonathonReinhart- นี่เป็นเพียงสิ่งที่ฉันคิดว่า (ก) เจ๋งจริงๆและ (ข) ไม่เป็นที่รู้จักกันดี ฉันดีใจที่ได้แบ่งปันสิ่งนี้เสมอ! :-)
templatetypedef

1
@ Mahesha999 กองมากเกินแข็งขันสนับสนุนให้ผู้ใช้ที่จะตอบคำถามของตัวเอง :-)
templatetypedef

เพียงแค่คาดเดาความหมายอื่น ๆ ที่ "ตอบคำถามของคุณเอง" ที่ด้านล่างของหน้าถามคำถามที่อาจมีอยู่หรือเพียงแค่เปิดใช้กล่องข้อความคำตอบในหน้าคำถาม
Mahesha999

1
บรรทัดสำคัญ: ดังนั้นหากมีอัลกอริทึมที่ทำงานโดยการลดปัญหาซ้ำ ๆ เป็นปัญหาย่อยของขนาดที่เป็นรากที่สองของขนาดปัญหาเดิมอัลกอริทึมนั้นจะสิ้นสุดหลังจากขั้นตอน O (log log n)
Gun2sh

3

วิธีหนึ่งในการดูปัจจัยของ O (log log n) ในความซับซ้อนของเวลาคือการหารเหมือนกับสิ่งที่อธิบายไว้ในคำตอบอื่น ๆ แต่มีอีกวิธีหนึ่งในการดูปัจจัยนี้เมื่อเราต้องการทำการแลกเปลี่ยนระหว่างเวลากับช่องว่าง / เวลา และการประมาณ / เวลาและความแข็ง / ... ของอัลกอริทึมและเรามีการทำซ้ำเทียมในอัลกอริทึมของเรา

ตัวอย่างเช่น SSSP (เส้นทางที่สั้นที่สุดของแหล่งเดียว) มีอัลกอริทึม O (n) บนกราฟระนาบ แต่ก่อนหน้านี้อัลกอริทึมที่ซับซ้อนนั้นมีอัลกอริทึมที่ง่ายกว่ามาก (แต่ก็ค่อนข้างยาก) ด้วยเวลาทำงาน O (n บันทึกบันทึก n) ฐานของอัลกอริทึมมีดังต่อไปนี้ (เป็นเพียงคำอธิบายที่หยาบมากและฉันขอเสนอให้ข้ามการทำความเข้าใจส่วนนี้และอ่านส่วนอื่น ๆ ของคำตอบ):

  1. แบ่งกราฟออกเป็นส่วนของขนาด O (log n / (log log n)) โดยมีข้อ จำกัด บางประการ
  2. สมมติว่าแต่ละส่วนที่กล่าวถึงคือโหนดในกราฟใหม่ G 'จากนั้นคำนวณ SSSP สำหรับ G' ในเวลา O (| G '| * log | G' |) ==> ที่นี่เพราะ | G '| = O (| G | * log log n / log n) เราสามารถเห็นปัจจัย (log log n)
  3. คำนวณ SSSP สำหรับแต่ละส่วน: อีกครั้งเนื่องจากเรามีส่วน O (| G '|) และเราสามารถคำนวณ SSSP สำหรับทุกส่วนได้ในเวลา | n / logn | * | log n / log logn * log (logn / log log n)
  4. อัปเดตน้ำหนักส่วนนี้สามารถทำได้ใน O (n) สำหรับรายละเอียดเพิ่มเติมเอกสารประกอบการบรรยายนี้ดี

แต่ประเด็นของฉันคือที่นี่เราเลือกการหารให้มีขนาด O (log n / (log log n)) หากเราเลือกหน่วยงานอื่นเช่น O (log n / (log n / (log n) ^ 2) ซึ่งอาจทำงานได้เร็วขึ้นและให้ผลลัพธ์อื่น ฉันหมายถึงในหลาย ๆ กรณี (เช่นในอัลกอริธึมการประมาณค่าหรืออัลกอริทึมแบบสุ่มหรืออัลกอริทึมเช่น SSSP ตามด้านบน) เมื่อเราวนซ้ำบางสิ่งบางอย่าง (ปัญหาย่อยวิธีแก้ปัญหาที่เป็นไปได้ ... ) เราจะเลือกจำนวนการทำซ้ำที่สอดคล้องกับการแลกเปลี่ยนนั้น เรามี (เวลา / ช่องว่าง / ความซับซ้อนของอัลกอริทึม / ปัจจัยคงที่ของอัลกอริทึม ... ) ดังนั้นเราอาจเห็นสิ่งที่ซับซ้อนมากกว่า "log log n" ในอัลกอริทึมการทำงานจริง

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