อัลกอริทึมที่มีประสิทธิภาพสำหรับปัญหาการมองเห็นในแนวตั้ง


18

ในระหว่างที่คิดถึงปัญหาหนึ่งฉันรู้ว่าฉันต้องสร้างอัลกอริทึมที่มีประสิทธิภาพในการแก้ไขงานต่อไปนี้:

ปัญหา:เราได้รับกล่องสี่เหลี่ยมสองมิติของด้านซึ่งด้านขนานกับแกน เราสามารถตรวจสอบมันผ่านด้านบน อย่างไรก็ตามยังมีส่วนแนวนอนแต่ละเซกเมนต์มีจำนวนเต็ม -coordinate ( ) และ -coordinates ( ) และเชื่อมต่อจุดและ (ดูที่ ภาพด้านล่าง)nmy0ynx0x1<x2n(x1,y)(x2,y)

เราอยากทราบว่าแต่ละเซ็กเมนต์ที่ด้านบนของกล่องเราจะมองลึกเข้าไปในแนวตั้งได้อย่างไรถ้ามองผ่านเซ็กเมนต์นี้

อย่างเป็นทางการสำหรับx{0,,n1}เราต้องการหาmaxi: [x,x+1][x1,i,x2,i]yiy_i

ตัวอย่าง: รับn=9และm=7กลุ่มตั้งอยู่ในภาพด้านล่างผลที่ได้คือ(5,5,5,3,8,3,7,8,7)7) ดูว่าแสงที่ลึกสามารถเข้าไปในกล่องได้

เจ็ดส่วน;  ส่วนที่แรเงาแสดงถึงพื้นที่ซึ่งสามารถเข้าถึงได้โดยแสง

โชคดีสำหรับเราทั้งnและmมีขนาดค่อนข้างเล็กและเราสามารถทำการคำนวณแบบออฟไลน์ได้

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

การปรับปรุงที่ดีมากคือการใช้แผนภูมิส่วนซึ่งสามารถเพิ่มค่าสูงสุดในส่วนในระหว่างการค้นหาและอ่านค่าสุดท้าย ผมจะไม่อธิบายมันต่อไป แต่เราจะเห็นว่าเวลาซับซ้อนคือn)O((m+n)logn)

อย่างไรก็ตามฉันคิดอัลกอริทึมที่เร็วกว่า:

เค้าร่าง:

  1. เรียงลำดับเซ็กเมนต์ตามลำดับที่ลดลงของ -coordinate (เวลาเชิงเส้นโดยใช้รูปแบบการเรียงลำดับการนับ) โปรดทราบว่าหากส่วนใดส่วนหนึ่งของหน่วยถูกครอบคลุมโดยส่วนใด ๆ ก่อนหน้านี้จะไม่มีส่วนต่อไปนี้ที่สามารถผูกลำแสงผ่านกลุ่มส่วนหน่วยนี้ จากนั้นเราจะทำการกวาดบรรทัดจากด้านบนไปด้านล่างของกล่องx xyxx

  2. ทีนี้เราจะมาแนะนำนิยาม: -unit เซ็กเมนต์เป็นส่วนแนวนอนจินตภาพในการกวาดซึ่ง -coordinates เป็นจำนวนเต็มและความยาวเท่ากับ 1 แต่ละเซกเมนต์ในระหว่างกระบวนการกวาดอาจไม่ถูกทำเครื่องหมาย (นั่นคือลำแสงจาก ด้านบนของกล่องสามารถเข้าถึงกลุ่มนี้) หรือทำเครื่องหมาย (กรณีตรงข้าม) พิจารณากลุ่ม -unit ที่มี ,ไม่ได้ทำเครื่องหมายเสมอ Let 's ยังแนะนำชุด\} แต่ละชุดจะมีลำดับทั้งหมดของเซ็กเมนต์ -unit ที่ทำเครื่องหมายติดต่อกัน(หากมี) ที่มีเครื่องหมายต่อไปนี้x x x 1 = n x 2 = n + 1 S 0 = { 0 } , S 1 = { 1 } , , S n = { n } xxxxx1=nx2=n+1S0={0},S1={1},,Sn={n} x ส่วน

  3. เราต้องการโครงสร้างข้อมูลที่สามารถใช้งานในเซกเมนต์เหล่านี้และตั้งค่าได้อย่างมีประสิทธิภาพ เราจะใช้โครงสร้าง find-union ที่ขยายโดยฟิลด์ที่เก็บดัชนีเซ็กเมนต์ -unit สูงสุด(ดัชนีของเซ็กเมนต์ที่ไม่ได้ทำเครื่องหมาย )x

  4. ตอนนี้เราสามารถจัดการส่วนต่างๆได้อย่างมีประสิทธิภาพ สมมติว่าตอนนี้เรากำลังพิจารณาส่วน -th ในการสั่งซื้อ (เรียกว่า "แบบสอบถาม") ซึ่งจะเริ่มขึ้นในและสิ้นสุดในx_2เราจำเป็นต้องค้นหาเซกเมนต์ -unit ที่ไม่ได้ทำเครื่องหมายซึ่งอยู่ในเซ็กเมนต์ th (นี่คือเซกเมนต์ที่ลำแสงจะสิ้นสุดลง) เราจะทำสิ่งต่อไปนี้: อันดับแรกเราจะพบกลุ่มที่ไม่มีเครื่องหมายในแบบสอบถาม ( ค้นหาตัวแทนของชุดที่มีอยู่และรับดัชนีสูงสุดของชุดนี้ซึ่งเป็นส่วนที่ไม่ได้ทำเครื่องหมายตามคำจำกัดความ ) จากนั้นดัชนีนี้x 1 x 2 x ฉันx 1 x y x x + 1 x x 2ix1x2 xix1xอยู่ในแบบสอบถามให้เพิ่มลงในผลลัพธ์ (ผลลัพธ์สำหรับส่วนนี้คือ ) และทำเครื่องหมายดัชนีนี้ ( ชุดยูเนี่ยนที่มีและ ) แล้วทำซ้ำขั้นตอนนี้จนกว่าเราจะพบว่าทุกป้ายส่วนที่เป็นต่อไปค้นหาแบบสอบถามจะช่วยให้เราดัชนีx_2yxx+1xx2

โปรดทราบว่าการดำเนินการค้นหาสหภาพจะดำเนินการในสองกรณีเท่านั้น: เราเริ่มพิจารณาเซ็กเมนต์ (ซึ่งอาจเกิดขึ้นครั้ง) หรือเราเพิ่งทำเครื่องหมายเซ็กเมนต์ -unit (สิ่งนี้สามารถเกิดขึ้นได้ครั้ง) ดังนั้นความซับซ้อนโดยรวมคือ (เป็นฟังก์ชัน Ackermann แบบผกผัน ) หากสิ่งที่ไม่ชัดเจนฉันสามารถอธิบายเพิ่มเติมเกี่ยวกับเรื่องนี้ บางทีฉันอาจจะสามารถเพิ่มรูปภาพได้ถ้าฉันมีเวลาx n O ( ( n + m ) α ( n ) ) αmxnO((n+m)α(n))α

ตอนนี้ฉันมาถึง "กำแพง" ฉันไม่สามารถคิดอัลกอริธึมเชิงเส้นได้ แต่ดูเหมือนว่าควรมีอย่างใดอย่างหนึ่ง ดังนั้นฉันมีสองคำถาม:

  • มีอัลกอริธึมเชิงเส้นเวลา (นั่นคือ ) ในการแก้ปัญหาการมองเห็นส่วนแนวนอนหรือไม่?O(n+m)
  • หากไม่เป็นเช่นนั้นอะไรคือข้อพิสูจน์ว่าปัญหาการมองเห็นคือ ?ω(n+m)

คุณจัดเรียงกลุ่มmของคุณเร็วแค่ไหน?
babou

@babou คำถามระบุการเรียงลำดับการนับซึ่งเป็นคำถามที่กล่าวว่าทำงานในเวลาเชิงเส้น ("เวลาเชิงเส้นโดยใช้รูปแบบของการเรียงลำดับการนับ")
DW

คุณลองกวาดจากซ้ายไปขวาใช่ไหม สิ่งที่คุณต้องมีคือการเรียงลำดับและทั้งในและขั้นตอนเพื่อเดินไปทางขวา ดังนั้นในการรวม(เมตร) x 2 O ( m ) O ( m ) O ( m )x1x2O(m)O(m)O(m)
invalid_id

@invalid_id ใช่ฉันพยายามแล้ว อย่างไรก็ตามในกรณีนี้บรรทัดการกวาดจะต้องตอบสนองอย่างเหมาะสมเมื่อตรงกับจุดเริ่มต้นของส่วน (กล่าวอีกนัยหนึ่งให้เพิ่มจำนวนเท่ากับส่วนของ -coordinate ไปยัง Multiset) ตรงตามจุดสิ้นสุดของส่วน (ลบการเกิดขึ้นของ -coordinate) และเอาต์พุตเซกเมนต์ที่แอ็คทีฟสูงสุด (เอาต์พุตสูงสุดในมัลติเซ็ต) ฉันไม่เคยได้ยินเกี่ยวกับโครงสร้างข้อมูลใด ๆ ที่ทำให้เราทำในเวลาคงที่ (ตัดจำหน่าย) Yyy
mnbvmar

@mnbvmar อาจจะเป็นข้อเสนอแนะที่เป็นใบ้ แต่วิธีการเกี่ยวกับอาร์เรย์ของขนาด , คุณกวาดและหยุดทุกเซลล์ (n) สำหรับเซลล์ evry ที่คุณรู้จัก maxและคุณสามารถป้อนลงในเมทริกซ์ได้นอกจากนี้คุณยังสามารถติดตามค่าสูงสุดโดยรวมด้วยตัวแปร O ( n ) ynO(n)y
invalid_id

คำตอบ:


1
  1. เรียงลำดับแรกทั้งและพิกัดของสายในสองอาร์เรย์แยกและBx 2 A B O ( m )x1x2ABO(m)
  2. นอกจากนี้เรายังรักษาขนาดบิตอาร์เรย์ auxilaryเพื่อติดตามกลุ่มที่ใช้งานอยู่n
  3. เริ่มกวาดจากซ้ายไปขวา:
  4. สำหรับ(ผม=0,ผม<n,ผม++)
  5. {
  6. ..if existด้วยค่าy c O ( 1 )x1=ผมY O(1)
  7. .. {
  8. .... ค้นหา ( )max
  9. .... store ( )O ( 1 )maxO(1)
  10. .. }
  11. ..if existด้วยค่าy c O ( 1 )x2=iyc O(1)
  12. .. {
  13. .... ค้นหา ( )max
  14. .... store ( )O ( 1 )maxO(1)
  15. .. }
  16. }

find ( ) สามารถนำมาใช้โดยใช้อาร์เรย์บิตที่มีบิตตอนนี้เมื่อใดก็ตามที่เราลบหรือเพิ่มองค์ประกอบไปยังเราสามารถอัปเดตจำนวนเต็มนี้โดยการตั้งค่าบิตเป็นจริงหรือเท็จตามลำดับ ตอนนี้คุณมีสองตัวเลือกขึ้นอยู่กับภาษาการเขียนโปรแกรมที่ใช้และข้อสันนิษฐานค่อนข้างเล็กกล่าวคือมีขนาดเล็กกว่าอย่างน้อย 64 บิตหรือจำนวนเต็มของจำนวนเต็มเหล่านี้:n L n L o n กรัมต่อลิตรo n กรัมฉันn TmaxnLnlonglongint

  • ได้รับบิตที่สำคัญน้อยที่สุดในเวลาคงที่ได้รับการสนับสนุนโดยฮาร์ดแวร์และ gcc
  • โดยการแปลงเป็นจำนวนเต็มคุณจะได้รับจำนวนสูงสุด (ไม่ใช่โดยตรง แต่คุณสามารถหามาได้)O ( 1 )LO(1)

ฉันรู้ว่ามันค่อนข้างแฮ็คเพราะถือว่าค่าสูงสุดสำหรับและด้วยเหตุนี้สามารถมองเห็นเป็นค่าคงที่แล้ว ...nnn


ที่ผมเห็นสมมติว่าคุณได้มีการประมวลผล x86 แบบ 64 บิตคุณสามารถที่จะจัดการเพียง64 เกิดอะไรขึ้นถ้าอยู่ในลำดับล้าน? nn64n
mnbvmar

จากนั้นคุณจะต้องมีจำนวนเต็มมากกว่านี้ ด้วยสองจำนวนเต็มคุณสามารถจัดการถึง 128 ฯลฯ ดังนั้นขั้นตอนการค้นพบสูงสุดที่ถูกซ่อนอยู่ในจำนวนของจำนวนเต็มที่จำเป็นที่คุณอาจจะยังคงเพิ่มประสิทธิภาพถ้าที่มีขนาดเล็ก คุณพูดถึงคำถามของคุณว่ามีขนาดค่อนข้างเล็กดังนั้นฉันจึงเดาได้ว่ามันไม่อยู่ในลำดับล้าน ๆ โดยทางยาว int นานอย่างน้อย 64 บิตตามคำนิยามแม้ในโปรเซสเซอร์ 32 บิต nO(m)mn
invalid_id

แน่นอนว่ามันเป็นจริงมาตรฐาน C ++ กำหนดlong long intเป็นประเภทจำนวนเต็มอย่างน้อย 64 บิต อย่างไรก็ตามจะไม่เป็นเช่นนั้นหรือไม่หากมีขนาดใหญ่และเราแสดงขนาดของคำว่า (ปกติคือ ) ดังนั้นแต่ละคำจะใช้เวลาใช่ไหม จากนั้นเราก็จะจบลงด้วยรวมขวา) nww=64findO(nw)O(mnw)
mnbvmar

ใช่น่าเสียดายสำหรับค่าขนาดใหญ่ของคือกรณีนี้ ดังนั้นตอนนี้ฉันสงสัยว่าขนาดใหญ่จะอยู่ในกรณีของคุณและไม่ว่าจะเป็นขอบเขต ถ้ามันเป็นจริงในการสั่งซื้อของล้านนี้สับรอบจะไม่ทำงานอีกต่อไป แต่ถ้าต่ำค่ามันจะเป็นจริงได้อย่างรวดเร็วและm) ดังนั้นตัวเลือกอัลกอริทึมที่ดีที่สุดคือตามปกติขึ้นอยู่กับอินพุต ตัวอย่างเช่นสำหรับจัดเรียงแทรกเป็นปกติได้เร็วขึ้นแล้วผสานเรียงลำดับแม้จะมีเวลาทำงานของเมื่อเทียบกับn) n c w n c O ( n + m ) n 100 O ( n 2 ) O ( n log n )nncwncO(n+m)n100O(n2)O(nlogn)
invalid_id

3
ฉันสับสนกับการจัดรูปแบบที่คุณเลือก คุณรู้ไหมว่าคุณพิมพ์รหัสได้ที่นี่ใช่ไหม?
กราฟิลส์

0

ฉันไม่มีอัลกอริธึมเชิงเส้น แต่อันนี้ดูเหมือนจะเป็น O (m log m)

จัดเรียงกลุ่มตามพิกัดและความสูงแรก ซึ่งหมายความว่า (x1, l1) มาก่อนเสมอ (x2, l2) เมื่อใดก็ตามที่ x1 <x2 นอกจากนี้ (x1, l1) ที่ความสูง y1 มาก่อน (x1, l2) ที่ความสูง y2 เมื่อใดก็ตามที่ y1> y2

สำหรับทุกชุดย่อยที่มีพิกัดแรกเหมือนกันเราจะทำสิ่งต่อไปนี้ ปล่อยให้ส่วนแรกเป็น (x1, L) สำหรับกลุ่มอื่น ๆ ทั้งหมดในส่วนย่อย: หากส่วนยาวกว่ากลุ่มแรกให้เปลี่ยนจาก (x1, xt) เป็น (L, xt) และเพิ่มลงในกลุ่มย่อย L ตามลำดับที่เหมาะสม ไม่เช่นนั้นจะลดลง สุดท้ายหากเซตย่อยถัดไปมีค่าพิกัดแรกน้อยกว่า L ให้แบ่ง (x1, L) เป็น (x1, x2) และ (x2, L) เพิ่ม (x2, L) ไปยังชุดย่อยถัดไปตามลำดับที่ถูกต้อง เราสามารถทำได้เพราะเซกเมนต์แรกในเซ็ตย่อยนั้นสูงกว่าและครอบคลุมช่วงจาก (x1, L) ส่วนใหม่นี้อาจเป็นส่วนที่ครอบคลุม (L, x2) แต่เราจะไม่ทราบจนกว่าเราจะดูที่ส่วนย่อยที่มีการประสานงานแรก L

หลังจากที่เราเรียกใช้ชุดย่อยทั้งหมดแล้วเราจะมีชุดของกลุ่มที่ไม่ทับซ้อนกัน ในการพิจารณาว่าค่า Y คืออะไรสำหรับ X ที่กำหนดเราจะต้องเรียกใช้ผ่านส่วนที่เหลือ

ความซับซ้อนคืออะไร: การเรียงลำดับคือ O (m log m) วนลูปผ่านชุดย่อยคือ O (m) การค้นหาคือ O (m)

ดังนั้นดูเหมือนว่าอัลกอริทึมนี้เป็นอิสระจาก n

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