อัลกอริทึมที่ไม่สำคัญสำหรับการคำนวณค่ามัธยฐานหน้าต่างบานเลื่อน


25

ฉันต้องการคำนวณค่ามัธยฐานที่ทำงานอยู่:

  • การป้อนข้อมูล: nn ,เวกเตอร์x_n)k k( x 1 , x 2 , , x n )(x1,x2,,xn)

  • เอาท์พุท:เวกเตอร์ที่เป็นค่ามัธยฐานของK-1})( ปี1 , ปี2 , ... , Y n - k + 1 ) (y1,y2,,ynk+1)Y ฉันyi ( x ฉัน , x ฉัน+ 1 , ... , x ฉัน+ k - 1 )(xi,xi+1,,xi+k1)

(ไม่มีการโกงโดยประมาณฉันต้องการคำตอบที่ถูกต้ององค์ประกอบเป็นจำนวนเต็มขนาดใหญ่)x ixi

มีอัลกอริทึมเล็ก ๆ น้อย ๆ ที่รักษาโครงสร้างการค้นหาขนาด ; เวลาทำงานรวมเป็นO (n \ บันทึก k) (นี่คือ "แผนผังการค้นหา" หมายถึงโครงสร้างข้อมูลที่มีประสิทธิภาพบางอย่างที่รองรับการแทรกการลบและการสอบถามค่ามัธยฐานในเวลาลอการิทึม)k kO ( n บันทึกk )O(nlogk)

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

เราสามารถทำอะไรที่ดีกว่าอย่างมีนัยสำคัญได้หรือไม่

มีขอบเขตที่ต่ำกว่า (เช่นอัลกอริธึมเล็กน้อยนั้นเหมาะที่สุดสำหรับการเปรียบเทียบแบบจำลองหรือไม่)


แก้ไข: David Eppstein ให้ขอบเขตล่างที่ดีสำหรับรุ่นเปรียบเทียบ! ฉันสงสัยว่ามันเป็นไปได้ไหมที่จะทำสิ่งที่ฉลาดกว่าอัลกอริทึมเล็กน้อย?

ตัวอย่างเช่นเราสามารถทำอะไรบางอย่างตามเส้นเหล่านี้ได้หรือไม่: แบ่งเวกเตอร์อินพุตให้เป็นส่วนของขนาดkk ; จัดเรียงแต่ละส่วน (ติดตามตำแหน่งเดิมของแต่ละองค์ประกอบ) แล้วใช้เวกเตอร์ที่เรียงลำดับตามเข็มเพื่อหาค่ามัธยฐานการวิ่งที่มีประสิทธิภาพโดยไม่มีโครงสร้างข้อมูลเสริม? แน่นอนว่านี่จะยังคงเป็นO ( n log k )O(nlogk)แต่ในการเรียงลำดับในทางปฏิบัติมีแนวโน้มที่จะเร็วกว่าการดูแลโครงสร้างการค้นหา


แก้ไข 2: Saeed ต้องการเห็นเหตุผลบางอย่างว่าทำไมฉันคิดว่าการเรียงลำดับเร็วกว่าการค้นหาทรี นี่คือมาตรฐานที่รวดเร็วมากสำหรับk = 10 7k=107 , n = 10 8n=108 :

  • ≈ 8s: การเรียงเวกเตอร์n / kn/kมีองค์ประกอบkkแต่ละอัน
  • ≈ 10s: การจัดเรียงเวกเตอร์ที่มีองค์ประกอบnn
  • ≈ 80:แทรกและลบในตารางแฮชขนาดn knk
  • ≈ 390S:แทรกและลบในต้นไม้ค้นหาสมดุลขนาดn knk

ตารางแฮชมีไว้เพื่อเปรียบเทียบเท่านั้น มันไม่มีประโยชน์โดยตรงในแอปพลิเคชันนี้

โดยสรุปเรามีความแตกต่างของประสิทธิภาพการเรียงลำดับกับการค้นหาทรีที่สมดุลเกือบ50เท่า และสิ่งที่ได้รับเลวร้ายมากถ้าเราเพิ่มkkk

(รายละเอียดทางเทคนิค: ข้อมูล = จำนวนเต็ม 32- บิตสุ่มคอมพิวเตอร์ = แล็ปท็อปทันสมัยทั่วไปรหัสทดสอบถูกเขียนใน C ++ โดยใช้ไลบรารีมาตรฐาน (std :: sort) และโครงสร้างข้อมูล (std :: multiset, std :: unsorted_multiset) ฉันใช้คอมไพเลอร์ C ++ สองตัว (GCC และ Clang) และการใช้งานที่แตกต่างกันสองอย่างของไลบรารี่มาตรฐาน (libstdc ++ และ libc ++) ตามเนื้อผ้า std :: multiset ได้รับการนำมาใช้เป็นต้นไม้สีแดงดำที่ได้รับการปรับให้เหมาะสมที่สุด)


1
ผมไม่คิดว่าคุณจะสามารถที่จะปรับปรุงn L o กรัม k เหตุผลคือถ้าคุณดูที่หน้าต่างx tnlogk , . . , x t + k - 1 , คุณไม่สามารถแยกแยะตัวเลขใด ๆ ได้เลย x t + kxt,...,xt+k12 , . . , x t + k - 1จากการเป็นค่ามัธยฐานของหน้าต่างในอนาคต ซึ่งหมายความว่าในเวลาใดก็ตามคุณต้องเก็บอย่างน้อย kxt+k2,...,xt+k12จำนวนเต็มในโครงสร้างข้อมูลและดูเหมือนว่าจะไม่อัปเดตในเวลาน้อยกว่าบันทึก k2
RB

อัลกอริธึมเล็กน้อยของคุณสำหรับฉันดูเหมือนว่าจะเป็นO ( ( n - k ) k log k )ไม่ใช่O ( n logO((nk)klogk) k ) , สิ่งที่ฉันเข้าใจผิด? และผมคิดว่าเพราะเหตุนี้คุณมีปัญหากับใหญ่ kมิฉะนั้นปัจจัยลอการิทึมอะไรในการใช้งานจริงยังไม่มีคงซ่อนใหญ่ในขั้นตอนวิธีนี้ O(nlogk)k
Saeed

@Saeed: ในอัลกอริทึมแบบเล็กน้อยคุณจะประมวลผลองค์ประกอบทีละชุด ในขั้นตอนที่ฉันคุณเพิ่มx iลงในแผนผังการค้นหาและ (ถ้าi > k ) คุณยังลบx i - kออกจากแผนผังการค้นหา นี่คือnขั้นตอนของแต่ละคนซึ่งจะใช้เวลาO ( บันทึกk )เวลา ixii>kxiknO(logk)
Jukka Suomela

ดังนั้นคุณหมายความว่าคุณมีแผนผังการค้นหาที่สมดุลไม่ใช่โครงสร้างการค้นหาทั่วไป?
Saeed

1
@Seed: โปรดทราบว่าในเกณฑ์มาตรฐานของฉันฉันไม่ได้พยายามหาค่าเฉลี่ย ผมก็ไม่ได้nแทรกและnลบในต้นไม้ค้นหาขนาดknnkและการดำเนินงานเหล่านี้มีการรับประกันที่จะใช้O ( บันทึกk )เวลา คุณต้องยอมรับว่าการดำเนินการค้นหาต้นไม้ช้ามากในทางปฏิบัติเมื่อเปรียบเทียบกับการเรียงลำดับ คุณจะเห็นสิ่งนี้ได้อย่างง่ายดายหากคุณพยายามเขียนอัลกอริทึมการเรียงลำดับที่ทำงานโดยการเพิ่มองค์ประกอบลงในแผนผังการค้นหาที่สมดุล - มันใช้งานได้ในเวลาO ( n log n )แต่มันจะช้าลงอย่างมากในทางปฏิบัติ ของหน่วยความจำ O(logk)O(nlogn)
Jukka Suomela

คำตอบ:


32

นี่คือขอบเขตที่ต่ำกว่าจากการเรียงลำดับ เมื่อกำหนดชุดอินพุตของS ที่มีความยาวnเพื่อจัดเรียงให้สร้างอินพุตสำหรับปัญหาค่ามัธยฐานที่ใช้อยู่ของคุณซึ่งประกอบด้วยn - 1สำเนาของจำนวนที่น้อยกว่าค่าต่ำสุดของSและตัวSเองจากนั้นn - 1สำเนาของจำนวนที่มากกว่า สูงสุดของSnn1SSn1 Sและชุด k = 2 n - 1 มีเดียการทำงานของการป้อนข้อมูลนี้เป็นเช่นเดียวกับเรียงลำดับของSSk=2n1S

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


6
คำตอบนี้ทำให้ฉันสงสัยว่าการสนทนานั้นดีหรือไม่: ด้วยอัลกอริธึมการเรียงลำดับที่มีประสิทธิภาพเราจะได้อัลกอริทึมมัธยฐานการรันที่มีประสิทธิภาพหรือไม่? (ตัวอย่างเช่นไม่เกี่ยวกับจำนวนเต็มเรียงลำดับขั้นตอนวิธีการที่มีประสิทธิภาพบ่งบอกถึงประสิทธิภาพการทำงานขั้นตอนวิธีการแบ่งจำนวนเต็มหรือไม่ขั้นตอนวิธีการเรียงลำดับ IO-ที่มีประสิทธิภาพให้ IO-ที่มีประสิทธิภาพการทำงานขั้นตอนวิธีการแบ่ง?)
Jukka Suomela

1
ขอขอบคุณอีกครั้งสำหรับคำตอบของคุณมันทำให้ฉันอยู่บนเส้นทางที่ถูกต้องและสร้างแรงบันดาลใจให้กับอัลกอริทึมการกรองแบบมัธยฐานตามการเรียงลำดับ! ในท้ายที่สุดฉันสามารถค้นหากระดาษจาก 1991 ซึ่งนำเสนอข้อโต้แย้งโดยทั่วไปเหมือนกับที่คุณให้ที่นี่และ Pat Morin ให้ตัวชี้ไปยังกระดาษอื่นที่เกี่ยวข้องจากปี 2005 ดูอ้างอิง [6] และ [9] ที่นี่
Jukka Suomela

9

แก้ไข:อัลกอริทึมนี้ถูกนำเสนอที่นี่: http://arxiv.org/abs/1406.1717


ใช่เพื่อแก้ปัญหานี้ก็เพียงพอที่จะดำเนินการต่อไปนี้:

  • จัดเรียงเวกเตอร์n / kแต่ละรายการมีองค์ประกอบkn/kk
  • ทำการโพสต์เวลาเชิงเส้น

คร่าวๆแนวคิดก็คือ:

  • พิจารณาทั้งสองกลุ่มที่อยู่ติดกัน, aและb , ทั้งสองมีองค์ประกอบk ; ให้องค์ประกอบจะ1 , 2 , . . , kและ1 , 2 , . . , b kตามลำดับลักษณะที่ปรากฏในอินพุตเวกเตอร์xabka1,a2,...,akb1,b2,...,bkx x
  • เรียงบล็อกเหล่านี้และเรียนรู้อันดับของแต่ละองค์ประกอบภายในบล็อก
  • เพิ่มเวกเตอร์aและbด้วยพอยน์เตอร์ของบรรพบุรุษ / ตัวตายตัวแทนเพื่อให้การติดตามตัวชี้โซ่เราสามารถสำรวจองค์ประกอบในลำดับที่เพิ่มขึ้น วิธีนี้เราได้สร้างรายการเชื่อมโยงทวีคูณ'และ 'abab
  • หนึ่งโดยหนึ่งลบองค์ประกอบทั้งหมดจากรายการที่เชื่อมโยง'ในลำดับย้อนกลับของการปรากฏตัวk , k - 1 , . . , b 1 เมื่อใดก็ตามที่เราลบองค์ประกอบจำสิ่งที่เป็นทายาทและบรรพบุรุษของมันในช่วงเวลาของการลบbbk,bk1,...,b1
  • ตอนนี้รักษา "ชี้ค่ามัธยฐาน" PและQที่ชี้ไปที่รายการ'และB 'ตามลำดับ initialise Pเพื่อจุดกึ่งกลางของ'และ Initialise Qหางของรายการที่ว่างเปล่า'pqabpaqb '
  • สำหรับแต่ละฉันi :

    • ลบฉันจากรายการ' (นี่คือO ( 1 )เวลาเพียงแค่ลบออกจากรายการที่เชื่อมโยง) เปรียบเทียบฉันกับองค์ประกอบที่ชี้โดยPเพื่อดูว่าเราลบออกก่อนหรือหลังพีaiaO(1)aippพี
    • นำb ฉันกลับไปที่รายการb ในตำแหน่งเดิม (นี่คือเวลาO ( 1 )เราจดจำผู้ที่มาก่อนและผู้สืบทอดของb i ) เปรียบเทียบฉันกับองค์ประกอบที่ชี้โดยคิวเพื่อดูว่าเราได้เพิ่มองค์ประกอบก่อนหรือหลังคิวbibO(1)bibiqq
    • ปรับปรุงตัวชี้PและQเพื่อให้ค่ามัธยฐานของรายการที่เข้าร่วม'B 'เป็นทั้งที่หน้าหรือQ (นี่คือเวลาO ( 1 )เพียงทำตามรายการที่เชื่อมโยงหนึ่งหรือสองขั้นตอนเพื่อแก้ไขทุกอย่างเราจะติดตามจำนวนรายการก่อน / หลังpและqในแต่ละรายการและเราจะรักษาค่าคงที่ที่pและคิวชี้ไปที่องค์ประกอบที่ใกล้เคียงกับค่าเฉลี่ยที่เป็นไปได้.)pqabpqO(1)pqpq

รายการที่เชื่อมโยงเป็นเพียงอาร์เรย์k -element ของดัชนีดังนั้นจึงมีน้ำหนักเบา (ยกเว้นว่าการเข้าถึงหน่วยความจำท้องถิ่นไม่ดี)k


นี่คือตัวอย่างการใช้งานและมาตรฐาน:

นี่คือพล็อตของเวลาทำงาน (สำหรับn 2 10 6 ):n2106

  • สีน้ำเงิน = การเรียงลำดับ + การโพสต์O ( n บันทึกk )O(nlogk)
  • Green = รักษาสอง heaps, O ( n log k ) , การนำไปใช้จากhttps://github.com/craffel/median-filterO(nlogk)
  • สีแดง = รักษาต้นไม้สองต้นค้นหาO ( n บันทึกk )O(nlogk)
  • = ดำรักษาเวกเตอร์เรียงO ( n k )O(nk)
  • แกน X = ขนาดหน้าต่าง ( k / 2k/2 )
  • แกน Y = ใช้เวลาเป็นวินาที
  • Data = จำนวนเต็ม 32 บิตและสุ่ม 64 บิตจากการแจกแจงแบบต่างๆ

running times


3

เมื่อพิจารณาถึงขอบเขตของเดวิดมันไม่น่าเป็นไปได้ที่คุณจะทำได้ดีที่สุดในกรณีที่เลวร้ายที่สุด โดยเฉพาะถ้าเมตรในจำนวนของมีเดียในผลที่เราสามารถแก้ปัญหาในเวลาO ( n log ม. + ม. เข้าสู่ระบบn )mO(nlogm+mlogn)

เมื่อต้องการทำสิ่งนี้แทนที่ต้นไม้ไบนารีที่สมดุลด้วยต้นไม้ไบนารีที่สมดุลซึ่งประกอบด้วยเฉพาะองค์ประกอบที่เป็นค่ามัธยฐานในอดีตบวกสองฟีโบนักชีกองสองตัวระหว่างแต่ละคู่ของค่ามัธยฐานก่อนหน้า (หนึ่งค่าสำหรับแต่ละทิศทาง) บวกจำนวนเพื่อให้เรา ค้นหาว่า Fibonacci heap ใดที่มีองค์ประกอบเฉพาะตามลำดับ ไม่ต้องกังวลกับการลบองค์ประกอบ เมื่อเราใส่องค์ประกอบใหม่ที่เราสามารถปรับปรุงโครงสร้างข้อมูลของเราในO ( บันทึกเมตร)เวลา หากการนับใหม่บ่งชี้ว่าค่ามัธยฐานอยู่ในหนึ่งในฟีโบนักชีกองหนึ่งก็จะใช้O ( log n ) เพิ่มเติมเพื่อดึงค่ามัธยฐานใหม่ออกมา นี้O ( บันทึกn )O(logm)O(logn)O(logn) ค่าใช้จ่ายเกิดขึ้นเพียงครั้งเดียวต่อค่ามัธยฐาน

If there was a clean way to delete elements without damaging the nice Fibonacci heap complexity, we'd get down to O(nlogm+mlogk)O(nlogm+mlogk), but I'm not sure if this is possible.


Oops, this doesn't work as written, since if you don't delete elements the counts won't reflect the new window. I'm not sure if it can be fixed, but I will leave the answer in case there is a way.
Geoffrey Irving

So I think this algorithm may in fact take O(nlogm)O(nlogm) if you delete nodes from the Fibonacci heaps, since the Fibonacci heap depth increases only when delete-min is called. Does anyone know nice bounds on Fibonacci heap complexity taking the number of delete-min calls into account?
Geoffrey Irving

side note: Question is not clear, underling data structure is not defined, we just know something very vague. how do you want improve something that you don't know what it is? how do you want compare your approach?
Saeed

1
I apologize for the incomplete work. I've asked the concrete question needed to fix this answer here: cstheory.stackexchange.com/questions/21778/…. If you think it's appropriate I can remove this answer until the secondary question is resolved.
Geoffrey Irving
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.