เหตุใดการผนวกรายการใน Scala จึงมีความซับซ้อนของเวลา O (n)


13

ฉันเพิ่งอ่านว่าเวลาดำเนินการของการดำเนินการผนวกสำหรับList(+) Listเติบโตขึ้นเป็นเส้นตรงกับขนาดของ

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

จากมุมมองของฉันทั้งการเตรียมและการต่อท้ายควรเป็น O (1)

มีเหตุผลที่ถูกต้องสำหรับสิ่งนี้หรือไม่?


2
ขึ้นอยู่กับคำจำกัดความของคุณของ "ถูกกฎหมาย" Scala นั้นมีโครงสร้างข้อมูลที่เปลี่ยนแปลงไม่ได้รายการที่ไม่ระบุชื่อที่แพร่หลายการจัดองค์ประกอบการทำงาน ฯลฯ การใช้งานรายการเริ่มต้น หากคุณต้องการรายการที่ทรงพลังยิ่งกว่านั้นอย่างน้อยก็ง่ายมากที่จะเขียนคอนเทนเนอร์ของคุณเองซึ่งแทบจะแยกไม่ออกจากรายการมาตรฐาน
Kilian Foth

1
ข้ามไซต์ที่เกี่ยวข้อง - ต่อท้ายองค์ประกอบในตอนท้ายของรายการใน Scala - มีบางอย่างในนั้นเกี่ยวกับลักษณะของมัน มันปรากฏว่ารายการในสกาล่าคือไม่เปลี่ยนรูปจึงทำให้คุณต้องคัดลอกมันซึ่งเป็น O (N)

คุณสามารถใช้หนึ่งในโครงสร้างข้อมูลที่ไม่แน่นอนที่มีอยู่จำนวนมากหรือโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปด้วย O (1) เวลาต่อท้าย (Vector) ที่ Scala จัดให้ List[T]สมมติว่าคุณใช้มันในแบบที่คุณต้องการด้วยภาษาที่ใช้งานได้จริง - โดยทั่วไปแล้วจะทำงานจากส่วนหัวด้วยโครงสร้างและส่วนเสริม
KChaloux

3
การจัดเตรียมจะใส่ตัวชี้โหนดถัดไปของส่วนหัวใหม่ไปยังรายการที่ไม่เปลี่ยนรูปแบบที่มีอยู่ซึ่งไม่สามารถเปลี่ยนแปลงได้ นั่นคือ O (1)

1
สำหรับงานน้ำเชื้อและการวิเคราะห์ในหัวข้อทั่วไปของการวัดความซับซ้อนของโครงสร้างข้อมูลใน FP บริสุทธิ์นั้นได้อ่านวิทยานิพนธ์ของ Okasakiที่ได้รับการตีพิมพ์ในภายหลังเป็นหนังสือ เป็นการอ่านที่ได้รับการยกย่องและดีมากสำหรับทุกคนที่เรียนรู้เกี่ยวกับ FP เพื่อทำความเข้าใจวิธีคิดเกี่ยวกับการจัดระเบียบข้อมูลใน FP นอกจากนี้ยังเขียนได้ดีและอ่านง่ายและทำตามดังนั้นข้อความคุณภาพทั้งหมด
จิมมี่ฮอฟฟา

คำตอบ:


24

ฉันจะขยายความคิดเห็นของฉันเล็กน้อย List[T]โครงสร้างข้อมูลจากscala.collection.immutableการเพิ่มประสิทธิภาพในการทำงานทางรายการไม่เปลี่ยนรูปในการทำงานมากขึ้นอย่างหมดจดผลงานการเขียนโปรแกรมภาษา มันมีอย่างรวดเร็วย่อหน้าครั้งและมันจะสันนิษฐานว่าคุณจะทำงานบนหัวเกือบทั้งหมดของการเข้าถึงของคุณ

รายการที่ไม่เปลี่ยนรูปจะมีเวลาเตรียมการที่รวดเร็วมากเนื่องจากพวกเขาจำลองรายการที่เชื่อมโยงเป็นชุดของ "cons cells" เซลล์กำหนดค่าเดียวและตัวชี้ไปยังเซลล์ถัดไป (สไตล์รายการแบบเชื่อมโยงโดยลำพังคลาสสิก):

Cell [Value| -> Nil]

เมื่อคุณเพิ่มเข้าไปในรายการคุณจะสร้างเซลล์ใหม่เพียงเซลล์เดียวโดยส่วนที่เหลือของรายการที่มีอยู่จะชี้ไปที่:

Cell [NewValue| -> [Cell[Value| -> Nil]]

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

สิ่งนี้ให้ยืมตัวเองดีมากในการทำงานซ้ำในรายการ สมมติว่าคุณกำหนดเวอร์ชันของคุณเองfilter:

def deleteIf[T](list : List[T])(f : T => Boolean): List[T] = list match {
  case Nil => Nil
  case (x::xs) => f(x) match {
    case true => deleteIf(xs)(f)
    case false => x :: deleteIf(xs)(f)
  }
}

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

หากคุณต้องการผนวกอย่างรวดเร็วจริง ๆ Scala มีโครงสร้างข้อมูลที่ไม่แน่นอนและไม่เปลี่ยนรูปแบบให้เลือกมากมาย ListBufferในด้านที่ไม่แน่นอนคุณอาจมองเข้าไปใน อีกทางเลือกหนึ่งVectorจากscala.collection.immutableมีเวลาผนวกที่รวดเร็ว


ตอนนี้ฉันเข้าใจ! มันทำให้รู้สึกสมบูรณ์
DPM

ฉันไม่รู้เรื่องสกาล่า แต่นั่นไม่ใช่elseวงวนอนันต์หรอ? x::deleteIf(xs)(f)ฉันคิดว่ามันควรจะเป็นสิ่งที่ชอบ
svick

@svick เอ่อ ... ใช่ ใช่แล้ว. ฉันเขียนมันอย่างรวดเร็วและไม่ได้ตรวจสอบรหัสของฉันเพราะฉันมีการประชุมไปที่: p (ควรได้รับการแก้ไขแล้ว!)
KChaloux

@Jubbat เพราะheadและtailการเข้าถึงรายการประเภทนี้นั้นรวดเร็วมาก - เร็วกว่าการใช้แผนที่หรืออาร์เรย์แบบแฮชใด ๆ - มันเป็นประเภทที่ยอดเยี่ยมสำหรับฟังก์ชั่นวนซ้ำ นี่คือเหตุผลหนึ่งที่ว่าทำไมรายการเป็นประเภทหลักในภาษาที่ใช้งานได้มากที่สุด (เช่น Haskell หรือ Scheme)
itbruce

คำตอบที่ยอดเยี่ยม ฉันอาจจะเพิ่ม TL; DR ที่เพียงแค่พูดว่า "เพราะคุณควรจะเตรียมการต่อท้ายไม่ได้ผนวก" (อาจช่วยล้างสมมติฐานพื้นฐานที่นักพัฒนาซอฟต์แวร์ส่วนใหญ่มีเกี่ยวกับLists และผนวก / เพิ่ม)
Daniel B
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.