เวลาตัดค่าคงที่อย่างต่อเนื่อง


คำตอบ:


776

เวลาค่าตัดจำหน่ายอธิบายในแง่ง่าย:

หากคุณทำการผ่าตัดหนึ่งล้านครั้งคุณจะไม่สนใจกรณีที่เลวร้ายที่สุดหรือกรณีที่ดีที่สุดของการดำเนินการนั้น - สิ่งที่คุณใส่ใจคือเวลาทั้งหมดที่ใช้ไปเมื่อคุณทำซ้ำเป็นล้านครั้ง .

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

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

ดังนั้นทุกครั้งที่คุณขยายคุณจะใช้เวลาประมาณสองเท่าของการขยายครั้งสุดท้าย แต่คุณก็ต้องรอนานกว่าสองเท่าก่อนจะทำ! ค่าใช้จ่ายของการขยายแต่ละครั้งจึงสามารถ "กระจาย" ในการแทรก ซึ่งหมายความว่าในระยะยาวรวมเวลาดำเนินการสำหรับการเพิ่มรายการที่จะอาร์เรย์O(m)และเพื่อให้เวลาตัดจำหน่าย (เช่นครั้งต่อแทรก) O(1)เป็น


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

1
ในแง่ของการจัดสรรพื้นที่นั่นมาจากกองเหรอ?
committedandroider

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

2
@Jeffpowrs ผมคิดว่า O (n) เป็นช่วงเวลาเชิงเส้นและ O (1) เป็นเวลาอย่างต่อเนื่อง นั่นหมายความว่า O (1) + จะถูกตัดจำหน่ายเวลาคงที่และ O (n) + จะถูกตัดจำหน่ายเป็นเส้นตรงหรือไม่
John Meyer

1
@JohnMeyer ใช่
Artelius

55

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

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

หรือในเงื่อนไขที่เข้มงวดมากขึ้น

มีค่าคงที่ c เช่นว่าสำหรับ ทุกลำดับของการดำเนินการ (เช่นเดียวกับที่ลงท้ายด้วยการดำเนินการที่มีราคาแพง) ความยาว L เวลาไม่เกิน c * L (ขอบคุณRafał Dowgird )


11
"หลังจากการดำเนินการจำนวนมากเพียงพอ" - เวลาที่ตัดจำหน่ายอย่างต่อเนื่องไม่ต้องการเงื่อนไขนี้ มีค่าคงที่ c เช่นว่าสำหรับทุกลำดับของการดำเนินการ (เช่นเดียวกับที่ลงท้ายด้วยการดำเนินการที่มีราคาแพง) ของความยาว L เวลาไม่เกิน c * L
Rafał Dowgird

นี่คือการจัดสรรสองเท่าของจำนวนที่มาจากไหน? เราไม่ควรจัดสรรให้หนึ่งรายการ? หรือเป็นตัวอย่างสมมุติฐาน?
talekeDskobeDa

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

23

ในการพัฒนาวิธีคิดที่เป็นธรรมชาติให้ลองพิจารณาการแทรกองค์ประกอบในอาร์เรย์แบบไดนามิก (ตัวอย่างเช่นstd::vectorใน C ++) ลองพล็อตกราฟที่แสดงการพึ่งพาจำนวนของการดำเนินการ (Y) ที่จำเป็นในการแทรกองค์ประกอบ N ในอาร์เรย์:

พล็อต

ส่วนแนวตั้งของกราฟสีดำสอดคล้องกับการจัดสรรหน่วยความจำใหม่เพื่อขยายอาร์เรย์ ที่นี่เราจะเห็นได้ว่าการขึ้นต่อกันนี้สามารถแสดงเป็นเส้นตรงได้ และสมการเส้นนี้คือY=C*N + b( Cคงที่b= 0 ในกรณีของเรา) ดังนั้นเราจึงสามารถพูดได้ว่าเราจำเป็นต้องใช้C*Nการดำเนินงานโดยเฉลี่ยเพื่อเพิ่มองค์ประกอบ N ไปยังอาร์เรย์หรือC*1การดำเนินงานเพื่อเพิ่มองค์ประกอบหนึ่ง (เวลาคงที่ตัดจำหน่าย)


14

ฉันพบคำอธิบาย Wikipedia ด้านล่างนี้มีประโยชน์หลังจากอ่านซ้ำ 3 ครั้ง:

ที่มา: https://en.wikipedia.org/wiki/Amortized_analysis#Dynamic_Array

"อาร์เรย์แบบไดนามิก

การวิเคราะห์ค่าตัดการดำเนินการกดสำหรับอาร์เรย์แบบไดนามิก

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

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

เพื่อความเข้าใจของฉันเป็นเรื่องง่าย:

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

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

เมื่อห้องหมดพื้นที่ เราต้องการห้องอื่นซึ่งใหญ่กว่า ที่นี่มีปัญหาเนื่องจากเราสามารถมีเพียง 1 ห้องเพื่อให้เราสามารถล็อคได้เพียง 1 เราจึงต้องย้ายเงินที่มีอยู่ทั้งหมดในห้องนั้นไปยังห้องใหม่ที่ใหญ่กว่า ดังนั้นย้ายเงินทั้งหมดจากห้องเล็กไปยังห้องที่ใหญ่กว่า นั่นคือกองทั้งหมดทั้งหมดอีกครั้ง ดังนั้นเราจำเป็นต้องย้ายเงินก่อนหน้านี้ทั้งหมด ดังนั้นมันคือ O (N) (สมมติว่า N คือจำนวนเงินทั้งหมดของเงินก่อนหน้า)

กล่าวอีกนัยหนึ่งมันง่ายจนถึง N เพียงปฏิบัติการเดียว แต่เมื่อเราต้องการย้ายไปยังห้องที่ใหญ่กว่า ถ้าเราเฉลี่ยมันก็คือ 1 ส่วนแทรกในการเริ่มต้นและอีก 1 ย้ายในขณะที่ย้ายไปที่ห้องอื่น รวมการดำเนินการ 2 การแทรกครั้งเดียวหนึ่งย้าย

สมมติว่า N มีขนาดใหญ่เช่น 1 ล้านแม้ในห้องเล็กการดำเนินการ 2 เมื่อเทียบกับ N (1 ล้าน) ไม่ได้เป็นจำนวนที่เปรียบเทียบได้ดังนั้นจึงถือว่าเป็นค่าคงที่หรือ O (1)

สมมติว่าเมื่อเราทำทั้งหมดข้างต้นในอีกห้องที่ใหญ่กว่าและต้องย้ายอีกครั้ง มันยังคงเหมือนเดิม พูด N2 (พูด 1 พันล้าน) เป็นจำนวนเงินใหม่ในห้องที่ใหญ่กว่า

ดังนั้นเรามี N2 (ซึ่งรวมถึง N จากก่อนหน้านี้เนื่องจากเราย้ายจากห้องเล็กไปใหญ่)

เรายังคงต้องการการดำเนินการเพียง 2 ครั้งโดยที่หนึ่งจะถูกแทรกเข้าไปในห้องที่ใหญ่กว่าและอีกการดำเนินการย้ายเพื่อย้ายไปยังห้องที่ใหญ่กว่า

ดังนั้นแม้สำหรับ N2 (1 พันล้าน) ก็เป็น 2 การดำเนินการสำหรับแต่ละ ซึ่งไม่มีอะไรอีกแล้ว ดังนั้นมันจึงเป็นค่าคงที่หรือ O (1)

ดังนั้นเมื่อ N เพิ่มขึ้นจาก N เป็น N2 หรืออื่น ๆ มันไม่สำคัญเท่าไหร่ มันยังคงที่หรือการดำเนินการ O (1) ที่จำเป็นสำหรับแต่ละ N


ทีนี้สมมติว่าคุณมี N เป็น 1 เล็กมากนับเงินน้อยและคุณมีห้องเล็กมากซึ่งจะพอดีกับเงินนับ 1

ทันทีที่คุณเติมเงินในห้องห้องนั้นก็จะเต็ม

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

ด้วยวิธีนี้ N เติบโตอย่างช้าๆและไม่มีค่าคงตัว O (1) เนื่องจากเราย้ายเงินทั้งหมดจากห้องก่อนหน้า แต่สามารถเติมเงินได้เพียง 1 เท่านั้น

หลังจาก 100 ครั้งห้องใหม่จะพอดีกับเงินนับ 100 จากก่อนหน้านี้และอีก 1 เงินที่สามารถรองรับได้ นี่คือ O (N) เนื่องจาก O (N + 1) คือ O (N) นั่นคือระดับ 100 หรือ 101 เหมือนกันทั้งสองเป็นร้อยเมื่อเทียบกับเรื่องราวก่อนหน้าของคนหลายล้านและหลายพันล้านคน .

ดังนั้นนี่เป็นวิธีที่ไม่มีประสิทธิภาพในการจัดสรรห้อง (หรือหน่วยความจำ / RAM) สำหรับเงินของเรา (ตัวแปร)


วิธีที่ดีคือการจัดสรรพื้นที่ให้มากขึ้นด้วยกำลัง 2

ขนาดห้องพัก 1 = พอดี 1 นับเงิน
ขนาดห้องพักที่ 2 = พอดี 4 นับเงิน
ขนาด 3 ห้อง = พอดี 8 นับเงิน
ขนาดห้องพักที่ 4 = พอดี 16 นับเงิน
ขนาดห้องพัก 5 = พอดี 32 นับเงิน
ขนาดห้องพัก 6 = พอดี 64 นับเงิน
ขนาดห้องพัก 7 = พอดี 128 นับเงิน
ขนาดห้องพัก 8 = พอดี 256 นับเงิน
ขนาดห้องพัก 9 = 512 เหมาะกับการนับเงิน
ขนาดห้องพัก 10 = 1024 เหมาะกับการนับเงิน
ขนาดห้องพัก 11 = 2,048
เหมาะกับการนับเงิน..
ขนาดห้องที่ 16 = พอดีกับ 65,536 นับเงิน
...
ขนาดห้องที่ 32 = เหมาะกับ 4,294,967,296 จำนวนเงิน
...
ขนาดห้องที่ 64 = เหมาะกับ 18,446,744,073,709,551,616 เงิน

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

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

อย่างไรก็ตามในกรณีสุดท้ายพลังของ 2 มันเติบโตในขอบเขตของ RAM ของเรา ดังนั้นการเพิ่มพลังของ 2 ทั้งการวิเคราะห์ Armotized จึงยังคงที่และเป็นมิตรกับ RAM ที่ จำกัด ที่เรามีอยู่ในปัจจุบัน


2
อาดังนั้นมันเป็น O (กรณีที่เลวร้ายที่สุด / # ของการดำเนินงาน) ฉันชอบคำตอบนี้ดีที่สุด
นิวคลีโอไทด์

1

คำอธิบายข้างต้นนำไปใช้กับการวิเคราะห์โดยรวมความคิดในการ "เฉลี่ย" เหนือการดำเนินการหลายอย่าง ฉันไม่แน่ใจว่าวิธีการเหล่านี้ใช้กับวิธีการของธนาคารหรือวิธีการทางฟิสิกส์ของการวิเคราะห์ค่าตัดจำหน่าย

ตอนนี้ ฉันไม่แน่ใจในคำตอบที่ถูกต้อง แต่มันจะต้องเกี่ยวข้องกับเงื่อนไขของหลักการของนักฟิสิกส์ + วิธีการของแบงเกอร์:

(ผลรวมของต้นทุนการดำเนินงานที่ตัดจำหน่าย)> = (ผลรวมของต้นทุนการดำเนินงานจริง)

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

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

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

ตัวอย่างเช่น: สำหรับฟีโบนักชีฮีปการอ้างถึงค่าตัดจำหน่ายของ Just Decreasing-Key เป็น O (1) นั้นไม่มีความหมายเนื่องจากค่าใช้จ่ายจะลดลงด้วย "งานที่ดำเนินการก่อนหน้านี้เพื่อเพิ่มศักยภาพของฮีป"

หรือ

เราอาจมีสมมติฐานอีกข้อหนึ่งที่ทำให้เกิดค่าใช้จ่ายตัดจำหน่ายดังนี้

  1. ฉันรู้ว่าการดำเนินการที่มีราคาแพงจะถูกนำหน้าด้วยการดำเนินการต้นทุนต่ำหลายรายการ

  2. เพื่อการวิเคราะห์ฉันจะคิดค่าใช้จ่ายในการดำเนินการที่มีต้นทุนต่ำเกินไปซึ่งหมายความว่าค่าใช้จ่ายที่ไม่ได้เปลี่ยนแปลงของพวกเขา

  3. ด้วยการดำเนินการต้นทุนต่ำที่เพิ่มขึ้นเหล่านี้ฉันสามารถพิสูจน์ได้ว่าการดำเนินการที่มีราคาแพงมีค่าใช้จ่ายที่น้อยกว่า

  4. ดังนั้นฉันจึงปรับปรุง / ลด ASYMPTOTIC-BOUND ของต้นทุนการดำเนินงาน n

ดังนั้นการวิเคราะห์ต้นทุนตัดจำหน่าย + ค่าตัดจำหน่ายต้นทุนขอบเขตตอนนี้ใช้กับการดำเนินงานที่มีราคาแพงเท่านั้น การดำเนินการราคาถูกมีค่าใช้จ่าย asymptotic ค่าตัดจำหน่ายเช่นเดียวกับค่า asymptotic ปกติของพวกเขา


ความคิดที่น่าสนใจ
Lonnie Best

0

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

ดังนั้นสาระสำคัญของฟังก์ชั่นที่ทำงานที่Constant Amortized Timeนี่คือ "เวลาเฉลี่ย" ถึงเพดานที่ไม่ได้รับเกินจำนวนการโทรยังคงเพิ่มขึ้น การโทรใด ๆ โดยเฉพาะอาจมีประสิทธิภาพแตกต่างกัน แต่ในระยะยาวเวลานี้โดยเฉลี่ยจะไม่เติบโตและยิ่งใหญ่ขึ้นเรื่อย ๆ

Constant Amortized Timeนี่คือบุญที่สำคัญของสิ่งที่ดำเนินการที่

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