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


106

ความรู้เกี่ยวกับ big-O ของฉันมี จำกัด และเมื่อคำบันทึกปรากฏขึ้นในสมการมันก็ยิ่งทำให้ฉันผิดหวัง

ใครช่วยอธิบายให้ฉันเข้าใจง่ายๆว่าO(log n)อัลกอริทึมคืออะไร? ลอการิทึมมาจากไหน?

สิ่งนี้เกิดขึ้นโดยเฉพาะเมื่อฉันพยายามแก้คำถามฝึกหัดกลางภาค:

ให้ X (1..n) และ Y (1..n) ประกอบด้วยรายการจำนวนเต็มสองรายการโดยแต่ละรายการเรียงลำดับแบบไม่ลดทอน ให้อัลกอริทึม - เวลา O (log n) เพื่อค้นหาค่ามัธยฐาน (หรือจำนวนเต็มน้อยที่สุดที่ n) ขององค์ประกอบที่รวมกัน 2n ทั้งหมด เช่น X = (4, 5, 7, 8, 9) และ Y = (3, 5, 8, 9, 10) จากนั้น 7 คือค่ามัธยฐานของรายการรวม (3, 4, 5, 5, 7 , 8, 8, 9, 9, 10) [คำแนะนำ: ใช้แนวคิดของการค้นหาทวิภาค]


29
O(log n)จะเห็นได้ว่า: หากคุณเพิ่มขนาดปัญหาเป็นสองเท่าnอัลกอริทึมของคุณต้องการจำนวนขั้นตอนที่คงที่มากขึ้นเท่านั้น
phimuemue

3
เว็บไซต์นี้ช่วยให้ฉันเข้าใจสัญลักษณ์ Big O: recursive-design.com/blog/2010/12/07/…
Brad

1
ฉันสงสัยว่าทำไม 7 ถึงเป็นค่ามัธยฐานของตัวอย่างข้างบน fwiw มันอาจเป็น 8 ก็ได้ เป็นตัวอย่างไม่ดีเหรอ?
stryba

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

ขอบคุณพวกเขาที่ทำงานกับปัญหาก่อนหน้านี้และจะดำเนินการให้เร็วที่สุดขอบคุณคำตอบมาก! จะกลับมาศึกษาในภายหลัง
user1189352

คำตอบ:


290

ฉันต้องยอมรับว่ามันค่อนข้างแปลกในครั้งแรกที่คุณเห็นอัลกอริทึม O (log n) ... ลอการิทึมนั้นมาจากไหนบนโลก? อย่างไรก็ตามปรากฎว่ามีหลายวิธีที่คุณจะได้รับข้อความบันทึกเพื่อแสดงในสัญกรณ์ขนาดใหญ่ นี่คือบางส่วน:

หารด้วยค่าคงที่ซ้ำ ๆ

ใช้จำนวนใด ๆ n; พูดว่า 16. คุณหาร n ด้วยสองได้กี่ครั้งก่อนที่คุณจะได้จำนวนน้อยกว่าหรือเท่ากับหนึ่ง? สำหรับ 16 เรามีสิ่งนั้น

16 / 2 = 8
 8 / 2 = 4
 4 / 2 = 2
 2 / 2 = 1

สังเกตว่าขั้นตอนนี้ต้องทำสี่ขั้นตอนให้เสร็จสิ้น ที่น่าสนใจคือเรามี log นั้นด้วย2 16 = 4 อืม ... แล้ว 128 ล่ะ?

128 / 2 = 64
 64 / 2 = 32
 32 / 2 = 16
 16 / 2 = 8
  8 / 2 = 4
  4 / 2 = 2
  2 / 2 = 1

นี่ใช้เวลาเจ็ดขั้นตอนและล็อก2 128 = 7 นี่เป็นเรื่องบังเอิญหรือเปล่า? ไม่! มีเหตุผลที่ดีสำหรับเรื่องนี้ สมมติว่าเราหารจำนวน n ด้วย 2 i คูณ จากนั้นเราได้รับจำนวน n / 2 ฉัน ถ้าเราต้องการแก้ค่าของ i โดยที่ค่านี้มากที่สุด 1 เราจะได้

n / 2 ฉัน ≤ 1

n ≤ 2 i

บันทึก2 n ≤ i

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

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

แล้วสิ่งนี้เกิดขึ้นที่ไหน? ตัวอย่างคลาสสิกอย่างหนึ่งคือค้นหาแบบไบนารีซึ่งเป็นอัลกอริทึมที่รวดเร็วสำหรับการค้นหาอาร์เรย์ที่เรียงลำดับเพื่อหาค่า อัลกอริทึมทำงานดังนี้:

  • หากอาร์เรย์ว่างเปล่าให้ส่งคืนองค์ประกอบที่ไม่มีอยู่ในอาร์เรย์
  • มิฉะนั้น:
    • ดูองค์ประกอบตรงกลางของอาร์เรย์
    • ถ้ามันเท่ากับองค์ประกอบที่เรากำลังมองหาจงคืนความสำเร็จ
    • หากมันมากกว่าองค์ประกอบที่เรากำลังมองหา:
      • ทิ้งครึ่งหลังของอาร์เรย์
      • ทำซ้ำ
    • หากมันน้อยกว่าองค์ประกอบที่เรากำลังมองหา:
      • ทิ้งครึ่งแรกของอาร์เรย์
      • ทำซ้ำ

ตัวอย่างเช่นหากต้องการค้นหา 5 ในอาร์เรย์

1   3   5   7   9   11   13

ก่อนอื่นเรามาดูองค์ประกอบตรงกลาง:

1   3   5   7   9   11   13
            ^

ตั้งแต่ 7> 5 และเนื่องจากอาร์เรย์ถูกจัดเรียงเราจึงรู้ว่าตัวเลข 5 ไม่สามารถอยู่ครึ่งหลังของอาร์เรย์ได้ดังนั้นเราจึงสามารถทิ้งมันไปได้ ใบนี้

1   3   5

ตอนนี้เรามาดูองค์ประกอบตรงกลางที่นี่:

1   3   5
    ^

ตั้งแต่ 3 <5 เรารู้ว่า 5 ไม่สามารถปรากฏในครึ่งแรกของอาร์เรย์ได้ดังนั้นเราจึงสามารถทิ้งอาร์เรย์ครึ่งแรกเพื่อออกไป

        5

อีกครั้งเราดูตรงกลางของอาร์เรย์นี้:

        5
        ^

เนื่องจากนี่เป็นตัวเลขที่เรากำลังมองหาเราจึงรายงานได้ว่า 5 อยู่ในอาร์เรย์

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

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

การประมวลผลค่าทีละหลัก

เลขฐาน 10 n มีกี่หลัก? ดีถ้ามีตัวเลข k ในจำนวนนั้นเราจะต้องว่าหลักที่ใหญ่ที่สุดคือหลาย 10 บางk จำนวน k หลักที่ใหญ่ที่สุดคือ 999 ... 9, k ครั้งและนี่เท่ากับ 10 k + 1 - 1 ดังนั้นถ้าเรารู้ว่า n มี k หลักอยู่เราจะรู้ว่าค่าของ n คือ มากที่สุด 10 k + 1 - 1 ถ้าเราต้องการแก้สำหรับ k ในรูปของ n เราจะได้

n ≤ 10 k + 1 - 1

n + 1 ≤ 10 k + 1

บันทึก10 (n + 1) ≤ k + 1

(บันทึก10 (n + 1)) - 1 ≤ k

จากที่เราได้ว่า k นั้นมีค่าประมาณลอการิทึมฐาน 10 ของ n กล่าวอีกนัยหนึ่งจำนวนหลักใน n คือ O (log n)

ตัวอย่างเช่นลองคิดถึงความซับซ้อนของการเพิ่มตัวเลขขนาดใหญ่สองตัวที่ใหญ่เกินไปที่จะใส่ลงในคำในเครื่อง สมมติว่าเรามีตัวเลขเหล่านั้นแสดงอยู่ในฐาน 10 และเราจะเรียกตัวเลขว่า m และ n วิธีหนึ่งในการเพิ่มพวกเขาคือใช้วิธีระดับโรงเรียน - เขียนตัวเลขทีละหลักจากนั้นทำงานจากขวาไปซ้าย ตัวอย่างเช่นในการเพิ่ม 1337 และ 2065 เราจะเริ่มต้นด้วยการเขียนตัวเลขออกมาเป็น

    1  3  3  7
+   2  0  6  5
==============

เราเพิ่มตัวเลขสุดท้ายและถือ 1:

          1
    1  3  3  7
+   2  0  6  5
==============
             2

จากนั้นเราจะเพิ่มหลักที่สองไปสุดท้าย ("สุดท้าย") และดำเนินการ 1:

       1  1
    1  3  3  7
+   2  0  6  5
==============
          0  2

ต่อไปเราจะเพิ่มตัวเลขที่สามถึงสุดท้าย ("antepenultimate"):

       1  1
    1  3  3  7
+   2  0  6  5
==============
       4  0  2

สุดท้ายเราเพิ่มหลักที่สี่ถึงสุดท้าย ("preantepenultimate ... ฉันรักภาษาอังกฤษ):

       1  1
    1  3  3  7
+   2  0  6  5
==============
    3  4  0  2

ตอนนี้เราทำงานหนักแค่ไหน? เราทำงานทั้งหมด O (1) ต่อหลัก (นั่นคือจำนวนงานคงที่) และมี O (สูงสุด {log n, log m}) หลักที่ต้องประมวลผล สิ่งนี้ให้ผลรวมของความซับซ้อน O (สูงสุด {log n, log m}) เนื่องจากเราจำเป็นต้องเยี่ยมชมแต่ละหลักในตัวเลขสองตัว

อัลกอริทึมจำนวนมากได้รับคำ O (log n) จากการทำงานทีละหลักในบางฐาน ตัวอย่างคลาสสิกคือradix sortซึ่งเรียงลำดับจำนวนเต็มทีละหลัก มีการจัดเรียงเรดิกซ์หลายรสชาติ แต่โดยปกติจะทำงานในเวลา O (n log U) โดยที่ U เป็นจำนวนเต็มมากที่สุดเท่าที่จะเป็นไปได้ซึ่งจะถูกจัดเรียง เหตุผลก็คือการเรียงลำดับแต่ละครั้งใช้เวลา O (n) และมีการทำซ้ำ O (log U) ทั้งหมดที่จำเป็นในการประมวลผลตัวเลข O (log U) แต่ละหลักของตัวเลขที่ใหญ่ที่สุดที่กำลังเรียงลำดับ อัลกอริทึมขั้นสูงจำนวนมากเช่นอัลกอริธึมเส้นทางที่สั้นที่สุดของ GabowหรืออัลกอริทึมการไหลสูงสุดของFord-Fulkersonรุ่นปรับขนาดจะมีคำบันทึกที่ซับซ้อนเนื่องจากทำงานทีละหลัก


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

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


8

เมื่อเราพูดถึงคำอธิบายขนาดใหญ่เรามักจะพูดถึงเวลาที่ใช้ในการแก้ปัญหาเกี่ยวกับขนาดที่กำหนดขนาดและโดยปกติสำหรับปัญหาง่ายๆขนาดนั้นจะมีลักษณะเฉพาะด้วยจำนวนองค์ประกอบอินพุตและโดยปกติจะเรียกว่า n หรือ N (แน่นอนว่าไม่จริงเสมอไป - ปัญหาเกี่ยวกับกราฟมักจะมีลักษณะเป็นจำนวนจุดยอด, V และ จำนวนขอบ E; แต่สำหรับตอนนี้เราจะพูดถึงรายการของวัตถุโดยมีวัตถุ N อยู่ในรายการ)

เราบอกว่าปัญหา "is big-Oh of (some function of N)" if and only if :

สำหรับ N> N_0 ตามอำเภอใจทั้งหมดมีค่าคงที่ c เช่นรันไทม์ของอัลกอริทึมน้อยกว่าค่าคงที่ c คูณ(บางฟังก์ชันของ N)

กล่าวอีกนัยหนึ่งคืออย่าคิดถึงปัญหาเล็ก ๆ ที่ "ค่าใช้จ่ายคงที่" ของการตั้งค่าปัญหาเป็นเรื่องสำคัญให้คิดถึงปัญหาใหญ่ ๆ และเมื่อคิดถึงปัญหาใหญ่ ๆ big-Oh ของ (บางฟังก์ชันของ N) หมายความว่าเวลารันยังน้อยกว่าเวลาคงที่ของฟังก์ชันนั้นเสมอ เสมอ.

ในระยะสั้นฟังก์ชันนั้นเป็นขอบเขตบนถึงค่าคงที่

ดังนั้น "big-Oh of log (n)" จึงหมายถึงสิ่งเดียวกันกับที่ฉันกล่าวข้างต้นยกเว้น "ฟังก์ชันบางอย่างของ N" จะถูกแทนที่ด้วย "log (n)"

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

คุณสามารถเลือกค่าคงที่ตามอำเภอใจให้เป็น c = 10 และถ้ารายการของคุณมีองค์ประกอบ N = 32 คุณก็สบายดี: 10 * log (32) = 50 ซึ่งมากกว่ารันไทม์ที่ 32 แต่ถ้า N = 64 , 10 * log (64) = 60 ซึ่งน้อยกว่ารันไทม์ที่ 64 คุณสามารถเลือก c = 100 หรือ 1,000 หรือ gazillion และคุณจะยังพบ N บางตัวที่ละเมิดข้อกำหนดนั้น กล่าวอีกนัยหนึ่งคือไม่มี N_0

หากเราทำการค้นหาแบบไบนารีเราจะเลือกองค์ประกอบตรงกลางและทำการเปรียบเทียบ จากนั้นเราโยนตัวเลขครึ่งหนึ่งออกไปแล้วทำอีกครั้งและอีกครั้งไปเรื่อย ๆ ถ้า N = 32 ของคุณคุณทำได้ประมาณ 5 ครั้งเท่านั้นซึ่งก็คือ log (32) หาก N = 64 ของคุณคุณสามารถทำได้ประมาณ 6 ครั้งเท่านั้นเป็นต้นตอนนี้คุณสามารถเลือกค่าคงที่ตามอำเภอใจนั้นได้ในลักษณะที่ความต้องการจะเป็นไปตามค่า N ขนาดใหญ่เสมอ

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

(หมายเหตุ: เกือบตลอดเวลา Log (N) หมายถึง log-base-สองซึ่งเป็นสิ่งที่ฉันคิดไว้ข้างต้น)


4

ในวิธีแก้ปัญหาต่อไปนี้บรรทัดทั้งหมดที่มีการเรียกซ้ำจะกระทำกับครึ่งหนึ่งของขนาดที่กำหนดของอาร์เรย์ย่อยของ X และ Y บรรทัดอื่น ๆ จะทำในเวลาคงที่ ฟังก์ชันวนซ้ำคือ T (2n) = T (2n / 2) + c = T (n) + c = O (lg (2n)) = O (lgn)

คุณเริ่มต้นด้วย MEDIAN (X, 1, n, Y, 1, n)

MEDIAN(X, p, r, Y, i, k) 
if X[r]<Y[i]
    return X[r]
if Y[k]<X[p]
    return Y[k]
q=floor((p+r)/2)
j=floor((i+k)/2)
if r-p+1 is even
    if X[q+1]>Y[j] and Y[j+1]>X[q]
        if X[q]>Y[j]
            return X[q]
        else
            return Y[j]
    if X[q+1]<Y[j-1]
        return MEDIAN(X, q+1, r, Y, i, j)
    else
        return MEDIAN(X, p, q, Y, j+1, k)
else
    if X[q]>Y[j] and Y[j+1]>X[q-1]
        return Y[j]
    if Y[j]>X[q] and X[q+1]>Y[j-1]
        return X[q]
    if X[q+1]<Y[j-1]
        return MEDIAN(X, q, r, Y, i, j)
    else
        return MEDIAN(X, p, q, Y, j, k)

3

คำบันทึกปรากฏขึ้นบ่อยมากในการวิเคราะห์ความซับซ้อนของอัลกอริทึม นี่คือคำอธิบายบางส่วน:

1. คุณเป็นตัวแทนของตัวเลขได้อย่างไร?

ให้ใช้เลข X = 245436 สัญกรณ์“ 245436” นี้มีข้อมูลโดยปริยายอยู่ การทำให้ข้อมูลนั้นชัดเจน:

X = 2 * 10 ^ 5 + 4 * 10 ^ 4 + 5 * 10 ^ 3 + 4 * 10 ^ 2 + 3 * 10 ^ 1 + 6 * 10 ^ 0

ซึ่งเป็นการขยายทศนิยมของตัวเลข ดังนั้นจำนวนข้อมูลขั้นต่ำที่เราต้องใช้แทนตัวเลขนี้คือ6หลัก นี่ไม่ใช่เรื่องบังเอิญเนื่องจากตัวเลขใด ๆ ที่น้อยกว่า10 ^ dสามารถแสดงเป็นdหลักได้

ต้องใช้ตัวเลขกี่หลักในการแทนค่า X? นั่นเท่ากับเลขชี้กำลังที่ใหญ่ที่สุดคือ 10 ใน X บวก 1

==> 10 ^ d> X
==> log (10 ^ d)> log (X)
==> d * log (10)> log (X)
==> d> log (X) // และบันทึกจะปรากฏขึ้น อีกครั้ง ...
==> d = ชั้น (log (x)) + 1

โปรดทราบว่านี่เป็นวิธีที่รัดกุมที่สุดในการแสดงตัวเลขในช่วงนี้ การลดลงใด ๆ จะนำไปสู่การสูญเสียข้อมูลเนื่องจากตัวเลขที่ขาดหายไปสามารถจับคู่กับตัวเลขอื่น ๆ 10 หมายเลขได้ ตัวอย่างเช่น: 12 * สามารถแมปกับ 120, 121, 122, …, 129

2. คุณค้นหาตัวเลขใน (0, N - 1) ได้อย่างไร?

การรับ N = 10 ^ d เราใช้การสังเกตที่สำคัญที่สุดของเรา:

จำนวนข้อมูลขั้นต่ำในการระบุค่าโดยไม่ซ้ำกันในช่วงระหว่าง 0 ถึง N - 1 = log (N) หลัก

นี่หมายความว่าเมื่อถูกขอให้ค้นหาตัวเลขในบรรทัดจำนวนเต็มตั้งแต่ 0 ถึง N - 1 เราจำเป็นต้องมี log (N) อย่างน้อยพยายามค้นหา ทำไม? อัลกอริทึมการค้นหาใด ๆ จะต้องเลือกหนึ่งหลักหลังจากนั้นอีกตัวหนึ่งในการค้นหาหมายเลขนั้น

จำนวนหลักขั้นต่ำที่ต้องเลือกคือบันทึก (N) ดังนั้นจำนวนขั้นต่ำของการดำเนินการเพื่อค้นหาตัวเลขในช่องว่างขนาด N คือ log (N)

คุณสามารถคาดเดาความซับซ้อนของลำดับของการค้นหาแบบไบนารีการค้นหาตามลำดับหรือการค้นหาแบบ Deca ได้หรือไม่?
ใช้O (เข้าสู่ระบบ (N))!

3. คุณเรียงชุดตัวเลขอย่างไร?

เมื่อถูกขอให้จัดเรียงชุดของตัวเลข A ลงในอาร์เรย์ B นี่คือสิ่งที่ดูเหมือน ->

อนุญาตองค์ประกอบ

ทุกองค์ประกอบในอาร์เรย์ดั้งเดิมจะต้องแมปกับดัชนีที่สอดคล้องกันในอาร์เรย์ที่เรียงลำดับ ดังนั้นสำหรับองค์ประกอบแรกเรามี n ตำแหน่ง ในการค้นหาดัชนีที่สอดคล้องกันอย่างถูกต้องในช่วงนี้ตั้งแต่ 0 ถึง n - 1 เราต้อง ... log (n) การดำเนินการ

องค์ประกอบถัดไปต้องการการดำเนินการบันทึก (n-1) บันทึกถัดไป (n-2) และอื่น ๆ ยอดรวมเป็น:

==> log (n) + log (n - 1) + log (n - 2) + ... + log (1)

การใช้ log (a) + log (b) = log (a * b),

==> log (n!)

ซึ่งสามารถประมาณได้กับ nlog (n) - n
ซึ่งก็คือO (n * log (n))!

ดังนั้นเราจึงสรุปได้ว่าไม่มีอัลกอริทึมการเรียงลำดับใดที่ทำได้ดีไปกว่า O (n * log (n)) และอัลกอริทึมบางอย่างที่มีความซับซ้อนนี้ก็คือ Merge Sort และ Heap Sort ที่เป็นที่นิยม!

นี่คือสาเหตุบางประการที่เราเห็น log (n) ปรากฏขึ้นบ่อยครั้งในการวิเคราะห์ความซับซ้อนของอัลกอริทึม สามารถขยายเป็นเลขฐานสองได้ ฉันทำวิดีโอไว้ที่นี่
เหตุใด log (n) จึงปรากฏบ่อยมากในระหว่างการวิเคราะห์ความซับซ้อนของอัลกอริทึม

ไชโย!


2

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


1

ยังไม่สามารถแสดงความคิดเห็นได้ ... มันคือ! คำตอบของ Avi Cohen ไม่ถูกต้องลอง:

X = 1 3 4 5 8
Y = 2 5 6 7 9

ไม่มีเงื่อนไขใดที่เป็นจริงดังนั้น MEDIAN (X, p, q, Y, j, k) จะตัดทั้งห้า สิ่งเหล่านี้เป็นลำดับที่ไม่ลดลงไม่ใช่ค่าทั้งหมดที่แตกต่างกัน

ลองใช้ตัวอย่างความยาวเท่ากันนี้ด้วยค่าที่แตกต่างกัน:

X = 1 3 4 7
Y = 2 5 6 8

ตอนนี้ MEDIAN (X, p, q, Y, j + 1, k) จะตัดทั้งสี่

ฉันเสนออัลกอริทึมนี้แทนเรียกด้วย MEDIAN (1, n, 1, n):

MEDIAN(startx, endx, starty, endy){
  if (startx == endx)
    return min(X[startx], y[starty])
  odd = (startx + endx) % 2     //0 if even, 1 if odd
  m = (startx+endx - odd)/2
  n = (starty+endy - odd)/2
  x = X[m]
  y = Y[n]
  if x == y
    //then there are n-2{+1} total elements smaller than or equal to both x and y
    //so this value is the nth smallest
    //we have found the median.
    return x
  if (x < y)
    //if we remove some numbers smaller then the median,
    //and remove the same amount of numbers bigger than the median,
    //the median will not change
    //we know the elements before x are smaller than the median,
    //and the elements after y are bigger than the median,
    //so we discard these and continue the search:
    return MEDIAN(m, endx, starty, n + 1 - odd)
  else  (x > y)
    return MEDIAN(startx, m + 1 - odd, n, endy)
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.