ฉันเข้าใจว่าการประชุมของ Tortoise และ Hare สรุปว่าการมีอยู่ของวงวนนั้นเป็นอย่างไร แต่การย้ายเต่าไปยังจุดเริ่มต้นของรายการที่เชื่อมโยงในขณะที่รักษากระต่าย ณ สถานที่ประชุมตามด้วยการย้ายทั้งสองขั้นในแต่ละครั้ง
ฉันเข้าใจว่าการประชุมของ Tortoise และ Hare สรุปว่าการมีอยู่ของวงวนนั้นเป็นอย่างไร แต่การย้ายเต่าไปยังจุดเริ่มต้นของรายการที่เชื่อมโยงในขณะที่รักษากระต่าย ณ สถานที่ประชุมตามด้วยการย้ายทั้งสองขั้นในแต่ละครั้ง
คำตอบ:
นี่คือขั้นตอนวิธีการของฟลอยด์สำหรับการตรวจสอบวงจร คุณกำลังถามเกี่ยวกับขั้นตอนที่สองของอัลกอริทึม - เมื่อคุณพบโหนดที่เป็นส่วนหนึ่งของวัฏจักรแล้วจะพบจุดเริ่มต้นของรอบได้อย่างไร
ในส่วนแรกของอัลกอริทึมของฟลอยด์กระต่ายเคลื่อนไหวสองขั้นตอนสำหรับทุกขั้นตอนของเต่า หากเต่าและกระต่ายเคยพบมีรอบและจุดนัดพบเป็นส่วนหนึ่งของวงจร แต่ไม่จำเป็นต้องเป็นโหนดแรกในรอบ
เมื่อเต่าและกระต่ายตอบสนองเราได้พบฉันที่เล็กที่สุด (จำนวนของขั้นตอนการดำเนินการโดยเต่าที่) เช่นว่า X ฉัน x = 2i ให้ mu แทนจำนวนขั้นตอนที่จะได้รับจาก X 0ถึงจุดเริ่มต้นของรอบและให้แลมบ์ดาแสดงความยาวของรอบ จากนั้น i = mu + a แลมบ์ดาและ 2i = mu + bแลมบ์ดาโดยที่ a และ b เป็นจำนวนเต็มแสดงถึงจำนวนเต่าและกระต่ายเดินไปรอบ ๆ กี่ครั้ง การลบสมการแรกจากสมการที่สองให้ i = (ba) * แลมบ์ดาดังนั้นฉันจึงเป็นจำนวนเต็มหลายแลมบ์ดา ดังนั้น X i + MU = Xหมู่ X iหมายถึงจุดนัดพบของเต่าและกระต่าย หากคุณย้ายเต่ากลับไปที่โหนดเริ่มต้น X0และปล่อยให้เต่าและกระต่ายดำเนินต่อไปด้วยความเร็วเดียวกันหลังจาก mu ขั้นตอนเพิ่มเติมเต่าจะมาถึง X muและกระต่ายจะมาถึง X i + mu = X muดังนั้นจุดนัดพบที่สองจึงเป็นจุดเริ่มต้นของ วงจร
X_mu
เริ่มต้นของรอบ (ตามคำนิยามของ mu) แล้วถ้าคุณใช้เวลาฉันขั้นตอนอื่น ๆ อีกมากมายที่ฉันมีหลายความยาวรอบที่คุณจะจบลงกลับมาที่จุดเริ่มต้นวงจร: =X_mu + i
X_mu
แต่การเพิ่มเป็นแบบสับเปลี่ยนดังนั้นนี่เทียบเท่ากับการทำตามขั้นตอน i เพื่อรับตั้งแต่จุดเริ่มต้นจนถึงจุดนัดพบครั้งแรกX_i
จากนั้นขั้นตอนเพิ่มเติมเพื่อกลับไปสู่X_mu
จุดเริ่มต้นของรอบ
i
คือในบางจุดของวงจรผมคิดว่าสมการที่ควรจะเป็นi = mu + k + a*lambda
และ2i = mu + k + b*lambda
ที่k
คือจำนวนของขั้นตอนตั้งแต่เริ่มต้นวงจรไปยังจุดที่ประชุม การลบสมการทั้งสองจะให้ผลลัพธ์เหมือนกัน
ให้ฉันพยายามอธิบายอัลกอริทึมการตรวจสอบรอบที่ให้ไว้ที่http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hareด้วยคำพูดของฉันเอง
มันทำงานอย่างไร
ให้เต่าและกระต่าย (ชื่อพอยน์เตอร์) ชี้ไปที่จุดเริ่มต้นของรายการด้วยวงจรเช่นเดียวกับในแผนภาพด้านบน
สมมุติว่าถ้าเราย้ายเต่าไปทีละขั้นตอนละหนึ่งก้าวและในอีกสองขั้นตอนพวกมันจะพบกันทีละจุด ขอแสดงให้เห็นว่าสมมติฐานข้อแรกทั้งหมดนี้เป็นจริง
ภาพแสดงรายการที่มีวงจร วงจรมีความยาวn
และเราอยู่m
ห่างจากรอบแรก สมมติว่าจุดนัดพบนั้นอยู่k
ห่างจากจุดเริ่มต้นของวงจรและเต่าและกระต่ายก็พบกันเมื่อเต่าทำi
ตามขั้นตอนทั้งหมดแล้ว (กระต่ายจะต้องทำ2i
ตามขั้นตอนทั้งหมดแล้ว)
เงื่อนไข 2 ข้อต่อไปนี้ต้องถือไว้:
1) i = m + p * n + k
2) 2i = m + q * n + k
คนแรกบอกว่าเต่าเคลื่อนที่i
ตามขั้นตอนและในi
ขั้นตอนเหล่านี้มันจะเข้าสู่รอบแรก จากนั้นก็จะไปผ่านวงจรครั้งสำหรับจำนวนบวกบางp
p
ในที่สุดมันจะไปยังk
โหนดอื่น ๆ จนพบกับกระต่าย
ที่คล้ายกันเป็นจริงสำหรับกระต่าย มันเคลื่อนที่2i
ตามขั้นตอนและใน2i
ขั้นตอนเหล่านี้ก่อนที่จะถึงรอบ จากนั้นก็จะไปผ่านวงจรครั้งสำหรับจำนวนบวกบางq
q
ในที่สุดมันจะไปยังk
โหนดอื่น ๆ จนกระทั่งพบกับเต่า
เมื่อกระต่ายเดินทางด้วยความเร็วเป็นสองเท่าของเต่าและเวลาก็คงที่เมื่อทั้งคู่มาถึงจุดนัดพบ
ดังนั้นโดยใช้ความสัมพันธ์ระหว่างความเร็วเวลาและระยะทางแบบง่าย ๆ
2 ( m + p * n + k ) = m + q * n + k
=> 2m + 2pn + 2k = m + nq + k
=> m + k = ( q - 2p ) n
ในบรรดา m, n, k, p, q สองคนแรกคือคุณสมบัติของรายการที่กำหนด หากเราสามารถแสดงให้เห็นว่ามีค่าอย่างน้อยหนึ่งชุดสำหรับ k, q, p ที่ทำให้สมการนี้เป็นจริงเราแสดงว่าสมมติฐานนั้นถูกต้อง
หนึ่งชุดโซลูชันดังกล่าวมีดังนี้:
p = 0
q = m
k = m n - m
เราสามารถตรวจสอบว่าค่าเหล่านี้ทำงานดังนี้:
m + k = ( q - 2p ) n
=> m + mn - m = ( m - 2*0) n
=> mn = mn.
สำหรับเซตนี้i
คือ
i = m + p n + k
=> m + 0 * n + mn - m = mn.
แน่นอนคุณควรเห็นว่านี่ไม่ใช่สิ่งที่ฉันต้องการที่สุด กล่าวอีกนัยหนึ่งเต่าและกระต่ายอาจพบกันมาหลายครั้งแล้ว อย่างไรก็ตามเนื่องจากเราแสดงให้เห็นว่าพวกเขาพบกันอย่างน้อยในบางครั้งเราสามารถพูดได้ว่าสมมติฐานนั้นถูกต้อง ดังนั้นพวกเขาจะต้องพบกันถ้าเราย้ายหนึ่งในนั้นหนึ่งขั้นตอนและอีกสองขั้นในเวลา
ตอนนี้เราสามารถไปที่ส่วนที่สองของอัลกอริทึมซึ่งเป็นวิธีหาจุดเริ่มต้นของวัฏจักร
รอบเริ่มต้น
เมื่อเต่าและกระต่ายมาพบกันให้นำเต่ากลับไปที่จุดเริ่มต้นของรายการและให้กระต่ายที่พวกเขาพบกัน (ซึ่งอยู่ห่างจากจุดเริ่มต้นของวงจร k)
สมมติฐานคือถ้าเราปล่อยให้พวกเขาเคลื่อนที่ด้วยความเร็วเดียวกัน (1 ขั้นตอนสำหรับทั้งคู่) ครั้งแรกที่พวกเขาพบกันอีกครั้งจะเป็นการเริ่มต้นรอบ
ลองพิสูจน์สมมติฐานนี้กัน
ก่อนอื่นสมมติว่า oracle บอกเราว่า m คืออะไร
จากนั้นถ้าเราปล่อยให้พวกเขาเคลื่อนที่ m + k ก้าวเต่าจะต้องมาถึงจุดที่พวกเขาพบกันในขั้นต้น (k ก้าวออกไปจากจุดเริ่มต้นของวงจร - ดูในรูป)
m + k = (q - 2p) n
ก่อนหน้านี้เราแสดงให้เห็นว่า
เนื่องจาก m + k ก้าวเป็นหลายรอบความยาว n, ในเวลาเฉลี่ย, จะผ่านรอบ (q-2p) ครั้งและจะกลับมาที่จุดเดียวกัน (k ก้าวออกจากจุดเริ่มต้นของรอบ)
ทีนี้แทนที่จะปล่อยให้พวกมันเคลื่อนที่ m + k ก้าวถ้าเราปล่อยให้พวกมันเคลื่อนที่เพียงแค่ก้าว m เท่านั้นเต่าจะมาถึงจุดเริ่มต้นของวงจร กระต่ายจะเป็นขั้นตอนสั้น ๆ ในการหมุน (q-2p) k เนื่องจากมันเริ่มต้น k ก้าวต่อหน้าวงรอบเริ่มต้นกระต่ายจึงต้องมาถึงจุดเริ่มต้นของวงจร
เป็นผลให้สิ่งนี้อธิบายว่าพวกเขาจะต้องพบกันที่วัฏจักรที่เริ่มต้นหลังจากขั้นตอนบางอย่างเป็นครั้งแรก (ครั้งแรกเพราะเต่าเพิ่งมาถึงรอบหลังจากขั้นตอน m และมันไม่เคยเห็นกระต่ายซึ่งอยู่ใน รอบ)
ตอนนี้เรารู้แล้วว่าจำนวนของขั้นตอนที่เราต้องย้ายพวกเขาจนกว่าพวกเขาจะพบกันกลายเป็นระยะทางจากจุดเริ่มต้นของรายการไปยังจุดเริ่มต้นของวงจรม. แน่นอนว่าอัลกอริทึมไม่จำเป็นต้องรู้ว่า m คืออะไร มันจะขยับทั้งเต่าและกระต่ายทีละก้าวจนกว่าพวกเขาจะพบกัน จุดนัดพบต้องเป็นการเริ่มรอบและจำนวนขั้นตอนต้องเป็นระยะทาง (m) ถึงจุดเริ่มต้นรอบ สมมติว่าเรารู้ความยาวของรายการเราสามารถคำนวณความยาวของวัฏจักรของการลบ m จากความยาวของรายการ
m + k = (q - 2p) n
สามารถทำให้ง่ายขึ้นไปm + k = q*n
อีก นี่เป็นเพราะจำนวนของเต่าลูปจะเป็นศูนย์เสมอเพราะกระต่ายไม่สามารถแซงหน้าเต่าได้โดยไม่ต้องพบมัน ลองคิดดู
อ้างอิงภาพนี้:
ระยะทางเดินทางโดย slowPointer ก่อนการประชุม = x + y
ระยะทางเดินทางโดย fastPointer ก่อนการประชุม = (x + y + z) + y = x + 2y + z
เนื่องจาก fastPointer เดินทางด้วยความเร็วเป็นสองเท่าของ slowPointer และเวลาคงที่สำหรับทั้งคู่เมื่อถึงจุดนัดพบ
ดังนั้นโดยใช้ความเร็วความสัมพันธ์ระหว่างเวลาและระยะทางที่เรียบง่าย 2 (x + y) = x + 2y + z => x + 2y + z = 2x + 2y => x = z
ดังนั้นโดยการย้ายslowPointerการเริ่มต้นของรายการที่เชื่อมโยงและทำให้ทั้งสอง slowPointer และ fastPointer ที่จะย้ายโหนดหนึ่งในเวลาที่พวกเขาทั้งสองมีระยะทางเดียวกันกับปก
พวกเขาจะไปถึงจุดที่ลูปเริ่มต้นในรายการที่เชื่อมโยง
คำตอบที่เรียบง่ายและต่ำกว่าต่ำของ Old Monkอธิบายการค้นหาวัฏจักรเมื่อนักวิ่งเร็วทำเพียงรอบเดียวเท่านั้น ในคำตอบนี้ฉันอธิบายกรณีเมื่อนักวิ่งเร็ววิ่งวนซ้ำหลายครั้งก่อนที่นักวิ่งช้าจะเข้าสู่วง
สมมุติว่านักวิ่งเร็ววิ่งวนรอบmก่อนที่จะเจอช้าและเร็ว ซึ่งหมายความว่า:
เนื่องจากการวิ่งที่รวดเร็วด้วยความเร็วของการช้าเป็นสองเท่าและมันวิ่งในเวลาเดียวกันนั่นก็หมายความว่าถ้าเราวิ่งเป็นสองเท่าของระยะทางที่ช้าเราจะได้ระยะทางที่เร็ว ดังนั้น
แก้เพื่อให้ x
x = (m - 1) (y + z) + z
ในสถานการณ์จริงมันจะหมายถึงx = (m - 1)ห่วงวิ่งสมบูรณ์ + ระยะห่างพิเศษZ
ดังนั้นถ้าเราวางตัวชี้หนึ่งไว้ที่จุดเริ่มต้นของรายการและปล่อยอีกจุดหนึ่งไว้ที่จุดนัดพบการเคลื่อนที่ด้วยความเร็วเดียวกันจะส่งผลให้ตัวชี้ในลูปจบการทำงานของm - 1ของการวนรอบแล้วพบอีกตัว ตัวชี้ไปทางขวาที่จุดเริ่มต้นของลูป
มันง่ายมาก คุณสามารถคิดในแง่ของความเร็วสัมพัทธ์ หากกระต่ายเคลื่อนที่สองโหนดและเต่าเคลื่อนที่ไปหนึ่งโหนดเมื่อเปรียบเทียบกับกระต่ายเต่านั้นจะเคลื่อนไหวไปหนึ่งโหนด (สมมติว่าเต่าพักตัว) ดังนั้นถ้าเราย้ายหนึ่งโหนดในรายการเชื่อมโยงแบบวงกลมเราจะไปพบกันที่จุดนั้นอีกครั้ง
หลังจากค้นหาจุดเชื่อมต่อภายในรายการเชื่อมโยงแบบวงกลมตอนนี้ปัญหาจะลดลงเป็นการค้นหาจุดตัดของปัญหารายการที่เชื่อมโยงสองรายการ
ในช่วงเวลาของการปะทะครั้งแรกเต่าขยับm + kก้าวตามที่แสดงข้างต้น กระต่ายเคลื่อนที่เร็วเป็นสองเท่าของเต่าหมายความว่ากระต่ายเคลื่อนไหว2 (m + k)ขั้นตอน จากข้อเท็จจริงง่ายๆเหล่านี้เราสามารถได้กราฟต่อไปนี้
ณ จุดนี้เราย้ายเต่ากลับไปที่จุดเริ่มต้นและประกาศว่าทั้งกระต่ายและเต่าจะต้องเคลื่อนที่ทีละก้าว ตามคำนิยามหลังจากขั้นตอนmเต่าจะเป็นจุดเริ่มต้นของรอบ กระต่ายจะอยู่ที่ไหน
กระต่ายก็จะเป็นจุดเริ่มต้นของรอบ นี่คือที่ชัดเจนจากกราฟที่สอง: เมื่อเต่าถูกย้ายกลับไปที่จุดเริ่มต้นกระต่ายคือkก้าวเข้าสู่รอบสุดท้าย หลังจากขั้นตอนmกระต่ายจะเสร็จสิ้นรอบอื่นและชนกับเต่า
วิธีการ:
มีสองพอยน์เตอร์:
หากตัวชี้สองตัวพบกันแสดงว่ามีการวนซ้ำ เมื่อพบกันแล้วโหนดใดโหนดหนึ่งจะชี้ไปที่ส่วนหัวจากนั้นให้ทั้งสองโหนดดำเนินการทีละโหนด พวกเขาจะพบกันที่จุดเริ่มต้นของวง
เหตุผล: เมื่อคนสองคนเดินตามทางเวียนหนึ่งในนั้นด้วยความเร็วสองเท่าของอีกคนหนึ่งพวกเขาจะพบกันที่ไหน พวกเขาเริ่มตรงไหน
ทีนี้สมมติว่านักวิ่งที่เร็วมีจุดเริ่มต้นk
ในn
รอบตัก พวกเขาจะพบกันที่ไหน ตรงn-k
ตามขั้นตอน เมื่อนักวิ่งที่ช้าครอบคลุม(n-k)
ขั้นตอนนักวิ่งที่เร็วจะได้ครอบคลุมk+2(n-k)
ขั้นตอน ( เช่นk+2n-2k
ขั้นตอนคือ2n-k
ขั้นตอน ) เช่น(n-k)
ขั้นตอน (เส้นทางเป็นแบบวงกลมและเราไม่กังวลเกี่ยวกับจำนวนรอบหลังจากที่พวกเขาพบกันเราเพียงแค่สนใจในตำแหน่งที่พวกเขาพบกัน)
ตอนนี้นักวิ่งเร็วจะเริ่มk
ก้าวแรกได้อย่างไร? เพราะมันใช้เวลานักวิ่งที่ช้าหลายขั้นตอนถึงจุดเริ่มต้นของวง ดังนั้นจุดเริ่มต้นของลูปคือ k ก้าวจากโหนดใหญ่
หมายเหตุ:โหนดที่ทั้งตัวชี้พบเป็นk
ขั้นตอนห่างจากจุดเริ่มต้นของลูป (ภายในวง) และโหนดหัวยังเป็นk
ขั้นตอนห่างจากจุดเริ่มต้นของลูป ดังนั้นเมื่อเรามีพอยน์เตอร์ที่ก้าวอย่างรวดเร็วเท่ากับ 1 ก้าวจากบอทโหนดเหล่านี้พวกมันจะพบกันที่จุดเริ่มต้นของลูป
ฉันเชื่อว่ามันตรงไปตรงมา โปรดแจ้งให้เราทราบหากส่วนใดไม่ชัดเจน
โอเคลองสมมติว่ากระต่ายและเต่ามาพบกันที่จุดซึ่งอยู่ห่างจากจุดเริ่มต้นของ k เป็นขั้นตอนจำนวนขั้นตอนก่อนที่วงจรจะเริ่มต้นคือ mu และความยาวของวงจรคือ L
ดังนั้น ณ จุดนัดพบ ->
ระยะทางที่ครอบคลุมโดยเต่า = mu + a * L + k - สมการ 1
(ขั้นตอนที่นำไปสู่จุดเริ่มต้นของรอบ + ขั้นตอนที่ดำเนินการเพื่อครอบคลุมการทำซ้ำ 'a' ของวงจร + ขั้นตอน k จากจุดเริ่มต้นของรอบ) (โดยที่ a คือค่าคงที่ที่เป็นบวก)
ระยะทางที่ครอบคลุมโดยกระต่าย = mu + b * L + k - สมการ 2
(ขั้นตอนนำไปสู่จุดเริ่มต้นของรอบ + ขั้นตอนที่ดำเนินการเพื่อครอบคลุมการทำซ้ำ 'b' ของรอบ + k ก้าวจากจุดเริ่มต้นของรอบ) (โดยที่ b คือค่าคงที่เป็นบวกและ b> = a)
ดังนั้นระยะทางพิเศษที่ครอบคลุมโดยกระต่ายคือ = สมการ 2 - สมการ 1 = (ba) * L
โปรดทราบว่าระยะทางนี้เท่ากับระยะทางของเต่าจากจุดเริ่มต้นเนื่องจากกระต่ายเคลื่อนไหวเร็วกว่าเต่า 2 เท่า สิ่งนี้สามารถเทียบได้กับ 'mu + k' ซึ่งเป็นระยะทางของจุดนัดพบตั้งแต่ต้นถ้าเราไม่รวมการสำรวจเส้นทางหลายรอบ
ดังนั้น mu + k = (ba) * L
ดังนั้น mu ก้าวจากจุดนี้จะนำกลับไปยังจุดเริ่มต้นของรอบ (ตั้งแต่ขั้นตอน k จากจุดเริ่มต้นของรอบได้ถูกนำไปถึงจุดนัดพบแล้ว) สิ่งนี้อาจเกิดขึ้นในรอบเดียวกันหรือรอบใด ๆ ที่ตามมา ดังนั้นตอนนี้ถ้าเราย้ายเต่าไปที่จุดเริ่มต้นของรายการที่เชื่อมโยงมันจะใช้เวลา mu เพื่อไปยังจุดเริ่มต้นของวงจรและกระต่ายจะใช้ขั้นตอน mu เพื่อไปยังจุดเริ่มต้นของวงจรและพวกเขาทั้งสองจะพบกันที่ จุดเริ่มต้นของวงจร
ป.ล. จริงๆแล้วฉันมีคำถามเดียวกันกับโปสเตอร์ต้นฉบับในใจของฉันและฉันอ่านคำตอบแรกพวกเขาล้างบางสิ่งออกไป แต่ฉันไม่สามารถรับผลลัพธ์สุดท้ายได้อย่างชัดเจนดังนั้นฉันจึงพยายามทำตามวิธีของตัวเองและ พบว่าง่ายต่อการเข้าใจ
ระยะการโทรจำนวนลิงก์ที่ตัวชี้ติดตามตามมาและเวลาที่จำนวนการวนซ้ำที่อัลกอริทึมใช้ในการเลื่อนตัวชี้แบบช้าหนึ่งลิงก์และตัวชี้แบบเร็วสองลิงก์ มีโหนด N ก่อนหน้าวงจรความยาว C มีป้ายกำกับด้วยวงจรออฟเซ็ต k = 0 ถึง C-1
ในการเข้าถึงจุดเริ่มต้นของวงจรการหมุนช้าจะใช้เวลา N และระยะ ซึ่งหมายความว่าเร็วใช้ระยะทาง N ในวงจร (N เพื่อไปที่นั่นและ N เพื่อหมุน) ดังนั้นที่เวลา N, ความช้าจะอยู่ที่วัฏจักรออฟเซ็ต k = 0, และความเร็วที่รอบออฟเซ็ต k = N mod C
ถ้า N mod C เป็นศูนย์ตอนนี้ช้าและเร็วจับคู่และรอบพบในเวลา N และตำแหน่งรอบ k = 0
ถ้า N mod C ไม่เป็นศูนย์ดังนั้นตอนนี้ fast จะต้องทันกับการช้าซึ่งในเวลานั้น N คือ C- (N mod C) ระยะทางที่อยู่ด้านหลังในรอบ
เนื่องจากการเคลื่อนที่อย่างรวดเร็ว 2 สำหรับทุก ๆ 1 ของการลดระยะทางโดย 1 ในการวนซ้ำทุกครั้งนี้ใช้เวลามากขึ้นเป็นระยะห่างระหว่างเร็วและช้าในเวลาที่ N ซึ่งเป็น C- (N mod C) เนื่องจากการเคลื่อนที่ช้าจากออฟเซ็ต 0 นี่จึงเป็นออฟเซ็ตที่พบ
ดังนั้นถ้า N mod C เป็นศูนย์เฟส 1 จะหยุดหลังจากการวนซ้ำ N ที่จุดเริ่มต้นของรอบ มิฉะนั้นเฟส 1 จะหยุดหลังจากการทำซ้ำ N + C- (N mod C) ที่ offset C- (N mod C) เข้าสู่วัฏจักร
// C++ pseudocode, end() is one after last element.
int t = 0;
T *fast = begin();
T *slow = begin();
if (fast == end()) return [N=0,C=0];
for (;;) {
t += 1;
fast = next(fast);
if (fast == end()) return [N=(2*t-1),C=0];
fast = next(fast);
if (fast == end()) return [N=(2*t),C=0];
slow = next(slow);
if (*fast == *slow) break;
}
ตกลงดังนั้นขั้นตอนที่ 2: ช้าใช้ N ขั้นตอนเพิ่มเติมเพื่อไปยังรอบที่จุดเร็ว (ตอนนี้ย้าย 1 ต่อขั้นตอนเวลา) อยู่ที่ (C- (N mod C) + N) mod C = 0 พวกเขาพบ ที่จุดเริ่มต้นของวงจรหลังเฟส 2
int N = 0;
slow = begin();
for (;;) {
if (*fast == *slow) break;
fast = next(fast);
slow = next(slow);
N += 1;
}
เพื่อความสมบูรณ์เฟส 3 คำนวณความยาวของวงจรโดยเลื่อนอีกครั้งผ่านวงจร:
int C = 0;
for (;;) {
fast = next(fast);
C += 1;
if (fast == slow) break;
}
ลดปัญหาให้เป็นปัญหาลูปจากนั้นกลับไปที่ปัญหาเริ่มต้น
ฉันพบว่าคำอธิบายต่อไปนี้ใช้งานง่ายขึ้น
ใช้เวลาสองชี้ ( 1 = เต่าและ2 = กระต่าย) ที่เริ่มต้นจากหัว ( O ) 1มีความยาวขั้นตอนของ1 , 2มีความยาวขั้นตอนของ2 ลองนึกถึงช่วงเวลาที่1ถึงโหนดเริ่มต้นของรอบนั้น ( A )
เราต้องการที่จะตอบคำถามต่อไปนี้"อยู่ที่ไหน 2 เมื่อ 1 อยู่ใน A".
ดังนั้นOA = a
เป็นจำนวนธรรมชาติ ( a >= 0
) แต่สามารถเขียนได้ด้วยวิธีต่อไปนี้: a = k*n + b
, ที่ไหนa, k, n, b are natural numbers
:
n
= ความยาวรอบk >= 0
= ค่าคงที่0 <= b <= n-1
มันหมายความว่า b = a % n
ก็หมายความว่า
เช่น: ถ้าa = 20
และn = 8
=> k = 2
และb = 4
เพราะ20 = 2*8 + 4
เพราะ
ระยะทางที่ครอบคลุมโดย1d = OA = a = k*n + b
คือ แต่ในเวลาเดียวกัน2D = 2*d = d + d = OA + d = OA + k*n + b
ครอบคลุม ซึ่งหมายความว่าเมื่อ2k*n + b
อยู่ในมันจะต้องมีฝาครอบ ในขณะที่คุณสามารถดูk
เป็นจำนวนรอบ แต่หลังจากรอบนั้น2จะขไกลจาก A. ดังนั้นเราพบที่2คือเมื่อ1อยู่ในโทร A. Let 's ที่จุดที่B
AB = b
ตอนนี้เราลดปัญหาเป็นวงกลม คำถามคือ"จุดนัดพบอยู่ที่ไหน" . คือว่าอยู่ที่ไหนC ?
ในทุกขั้นตอน2ลดระยะทางจาก1ด้วย1
(สมมติว่ามิเตอร์) เพราะ1เพิ่มขึ้นจาก2ด้วย1
แต่ในเวลาเดียวกัน2เข้าใกล้1โดย2
โดย
ดังนั้นทางแยกจะเป็นเมื่อระยะทางระหว่าง1ถึง2จะเป็นศูนย์ ซึ่งหมายความว่า2ช่วยลดn - b
ระยะทาง เพื่อให้บรรลุนี้1จะทำn - b
ตามขั้นตอนในขณะที่2จะทำ2*(n - b)
ตามขั้นตอน
ดังนั้นจุดตัดจะn - b
ห่างไกลจาก(ตามเข็มนาฬิกา) เพราะนี้คือระยะทางที่ครอบคลุมโดย1จนกว่าจะได้พบกับ2 => ระยะห่างระหว่างCและเป็นเพราะและ อย่าคิดว่าเพราะระยะทางไม่ใช่ระยะทางคณิตศาสตร์ที่น่าสนใจมันเป็นจำนวนขั้นตอนระหว่างAและC (โดยที่Aคือจุดเริ่มต้นและCคือจุดสิ้นสุด)CA = b
AC = AB + BC = n - b
CA = n - AC
AC = CA
AC
ทีนี้กลับไปที่สคีมาเริ่มต้นกัน
เรารู้ว่าa = k*n + b
และCA = b
และ
เราสามารถใช้ตัวชี้ 2 ใหม่1 'และ1' 'โดยที่1'เริ่มจากหัว ( O ) และ1 ''เริ่มจากจุดตัด ( C) )
ในขณะที่1 'ไปจากOถึงA , 1' 'ไปจากCถึงAและสิ้นสุดk
รอบต่อไป ดังนั้นจุดตัดคือ
หากตัวชี้พบที่จุด P ดังแสดงในรูประยะทาง Z + Y คือจุด P และ X + Y เป็นจุด P ซึ่งหมายถึง Z = X ซึ่งเป็นสาเหตุที่ทำให้การย้ายตัวชี้หนึ่งจาก P และการย้ายอีกครั้งจากจุดเริ่มต้น (S) จนกระทั่งพวกเขาพบซึ่งหมายความว่าการย้ายระยะทางเท่ากับ (Z หรือ X) ไปยังจุดเดียวกัน M (ระยะทาง Z จาก P และ X จาก S) จะ เริ่มจากวนรอบ ! ง่าย
จากการวิเคราะห์ทั้งหมดข้างต้นหากคุณเป็นบุคคลที่เรียนรู้โดยตัวอย่างฉันพยายามเขียนการวิเคราะห์สั้น ๆ และตัวอย่างที่ช่วยอธิบายคณิตศาสตร์ที่ทุกคนพยายามอธิบาย ไปเลย!
วิเคราะห์:
หากเรามีพอยน์เตอร์สองตัวตัวใดตัวหนึ่งเร็วกว่าตัวอื่นและย้ายพวกมันไปด้วยกันพวกมันจะพบกันอีกครั้งเพื่อระบุวัฏจักรหรือโมฆะเพื่อระบุว่าไม่มีวัฏจักรใด
เพื่อหาจุดเริ่มต้นของวัฏจักรปล่อยให้ ...
m
เป็นระยะทางจากหัวถึงจุดเริ่มต้นของวงจร
d
เป็นจำนวนโหนดในวงจร
p1
เป็นความเร็วของตัวชี้ช้า;
p2
เป็นความเร็วของตัวชี้ที่เร็วขึ้นเช่น 2 หมายถึงขั้นตอนผ่านสองโหนดต่อครั้ง
สังเกตการทำซ้ำต่อไปนี้:
m = 0, d = 10: p1 = 1: 0 1 2 3 4 5 6 7 8 9 10 // 0 would the start of the cycle p2 = 2: 0 2 4 6 8 10 12 14 16 18 20 m = 1, d = 10: p1 = 1: -1 0 1 2 3 4 5 6 7 8 9 p2 = 2: -1 1 3 5 7 9 11 13 15 17 19 m = 2, d = 10: p1 = 1: -2 -1 0 1 2 3 4 5 6 7 8 p2 = 2: -2 0 2 4 6 8 10 12 14 16 18
จากข้อมูลตัวอย่างข้างต้นเราสามารถค้นพบได้อย่างง่ายดายว่าเมื่อใดก็ตามที่ตัวชี้เร็วขึ้นและช้าลงจะพบพวกเขาอยู่m
ห่างจากจุดเริ่มต้นของวงจร ในการแก้ปัญหานี้ให้วางตัวชี้ที่เร็วกว่ากลับมาที่หัวและตั้งค่าความเร็วเป็นความเร็วของตัวชี้ที่ช้ากว่า เมื่อพวกเขาพบกันอีกครั้งโหนดคือจุดเริ่มต้นของวงจร
สมมติว่า
N[0] is the node of start of the loop,
m is the number of steps from beginning to N[0].
เรามี 2 พอยน์เตอร์ A และ B, A วิ่งที่ 1x speed, B ที่ 2x speed, ทั้งสองเริ่มต้นที่จุดเริ่มต้น
เมื่อ A ถึง N [0], B ควรอยู่ใน N [m] (หมายเหตุ: A ใช้ขั้นตอน m ในการเข้าถึง N [0] และ B ควรเป็นขั้นตอน m เพิ่มเติม)
จากนั้น A จะวิ่งไปอีกก้าวหนึ่งเพื่อชนกับ B นั่นคือ A อยู่ที่ N [k], B อยู่ที่ N [m + 2k] (หมายเหตุ: B ควรวิ่งเป็นระยะ 2k เริ่มต้นจาก N [m])
การชน B ที่ N [k] และ N [m + 2k] ตามลำดับมันหมายถึง k = m + 2k ดังนั้น k = -m
ดังนั้นในการวนกลับไปที่ N [0] จาก N [k] เราต้องมีขั้นตอนมากขึ้น
เพียงแค่บอกว่าเราเพียงแค่เรียกใช้ขั้นตอนเพิ่มเติมหลังจากที่เราพบโหนดการชน เราสามารถมีตัวชี้ให้เรียกใช้จากจุดเริ่มต้นและตัวชี้ที่เรียกใช้จากโหนดการชนกันพวกเขาจะพบกันที่ N [0] หลังจากขั้นตอน m
ดังนั้นรหัสหลอกมีดังนี้:
1) A increase 1 step per loop
2) B increase 2 steps per loop
3) if A & B are the same node, cycle found, then go to 5
4) repeat from 1
5) A reset to head
6) A increase 1 step per loop
7) B increase 1 step per loop
8) if A & B are the same node, start of the cycle found
9) repeat from 6
ฉันไม่คิดว่ามันจริงที่เมื่อพวกเขาพบว่าเป็นจุดเริ่มต้น แต่ใช่ถ้าตัวชี้อื่น ๆ (F) เคยอยู่ที่จุดนัดพบก่อนหน้านั้นตัวชี้นั้นจะอยู่ที่จุดสิ้นสุดของลูปแทนที่จะเป็นจุดเริ่มต้นของลูปและตัวชี้ (S) ซึ่งเริ่มต้นจากจุดเริ่มต้นของรายการ จบที่จุดเริ่มต้นของวง สำหรับเช่น:
1->2->3->4->5->6->7->8->9->10->11->12->13->14->15->16->17->18->19->20->21->22->23->24->8
Meet at :16
Start at :8
public Node meetNodeInLoop(){
Node fast=head;
Node slow=head;
fast=fast.next.next;
slow=slow.next;
while(fast!=slow){
fast=fast.next;
fast=fast.next;
if(fast==slow) break;
slow=slow.next;
}
return fast;
}
public Node startOfLoop(Node meet){
Node slow=head;
Node fast=meet;
while(slow!=fast){
fast=fast.next;
if(slow==fast.next) break;
slow=slow.next;
}
return slow;
}
คำอธิบายง่ายๆโดยใช้แนวคิดเรื่องความเร็วสัมพัทธ์ที่สอนในโรงเรียนมัธยม - วิชาฟิสิกส์ 101 / Kinematics
สมมติว่าระยะทางจากจุดเริ่มต้นของรายการที่เชื่อมโยงไปยังจุดเริ่มต้นของวงกลมคือx
ฮ็อพ มาเรียกจุดเริ่มต้นของวงกลมว่าเป็นจุดX
(เป็นตัวพิมพ์ใหญ่ - ดูรูปด้านบน) สมมุติว่าขนาดทั้งหมดของวงกลมคือ N hops
ความเร็วของกระต่าย = 2 * ความเร็วของเต่า นั่นคือ1 hops/sec
และ2 hops/sec
ตามลำดับ
เมื่อเต่ามาถึงจุดเริ่มต้นของวงกลมX
กระต่ายจะต้องx
กระโดดต่อไปที่จุดY
ในรูป (เพราะกระต่ายได้เดินทางไกลเป็นสองเท่าของเต่า)
ดังนั้นระยะเวลาที่เหลือโค้งตามเข็มนาฬิกาจาก X เป็น Y N-x
จะเป็น T ของเขายังเกิดขึ้นเป็นระยะทางญาติได้รับการคุ้มครองระหว่างกระต่ายกับเต่าสำหรับพวกเขาที่จะสามารถตอบสนองความต้องการ สมมติว่าระยะทางสัมพัทธ์นี้จะครอบคลุมในเวลาt_m
เช่นเวลาที่จะตอบสนอง ความเร็วสัมพัทธ์คือคือ(2 hops/sec - 1 hops/sec)
1 hops/sec
ดังนั้นการใช้, ระยะทางสัมพัทธ์ = เวลาสัมพัทธ์ X เราจะได้, t
= N-x
วินาที ดังนั้นมันจะN-x
ไปถึงจุดนัดพบสำหรับทั้งเต่าและกระต่าย
ตอนนี้ในN-x
เวลาวินาทีและ1 hops/sec
ความเร็วเต่าที่ก่อนหน้านี้ที่จุดX
จะครอบคลุม Nx M
กระโดดไปถึงจุดนัดพบ ดังนั้นนั่นหมายความว่าจุดนัดพบM
อยู่ที่N-x
hops ทวนเข็มนาฬิกาจากX
= (ซึ่งหมายถึงเพิ่มเติม) => ว่ามีx
ระยะทางที่เหลือจากจุดหนึ่งM
ไปยังอีกมุมหนึ่งX
ตามเข็มนาฬิกา
แต่x
ยังเป็นระยะทางถึงจุดX
เริ่มต้นของรายการที่ลิงก์
ตอนนี้เราไม่สนใจว่าจำนวนฮ็อพx
ตรงกับอะไร หากเราใส่เต่าหนึ่งตัวที่จุดเริ่มต้นของ LinkedList และเต่าหนึ่งตัวที่จุดนัดพบM
และปล่อยให้พวกเขากระโดด / เดินพวกเขาจะพบกันที่จุดX
ซึ่งเป็นจุด (หรือโหนด) ที่เราต้องการ
การทำงานกับไดอะแกรมจะช่วยได้ ฉันพยายามอธิบายปัญหาโดยปราศจากสมการ
- มี k ก้าวก่อนลูป เราไม่รู้ว่า k คืออะไรและไม่จำเป็นต้องค้นหา เราสามารถทำงานอย่างเป็นนามธรรมด้วยเพียง k
- หลังจากขั้นตอน k
----- T อยู่ที่จุดเริ่มต้นของรอบ
----- H คือ k ก้าวเข้าสู่วงจร (เขารวม 2k และทำให้ k เข้าสู่วง)
** พวกมันแยกออกจากกันแล้ว - แยก
(โปรดทราบว่า k == K == mod (loopsize, k) - หากว่าโหนดเป็น 2 ขั้นตอนในวัฏจักร 5 โหนดมันก็เป็นขั้นตอน 7, 12 หรือ 392 ดังนั้น wrt k จึงไม่ใหญ่ ปัจจัยใน
เนื่องจากพวกเขาติดต่อกันด้วยอัตรา 1 ขั้นต่อหน่วยของเวลาเนื่องจากมีการเคลื่อนที่เร็วเป็นสองเท่าเมื่อเปรียบเทียบกันพวกเขาจะพบกันที่ลูป - k
ซึ่งหมายความว่าจะต้องใช้โหนด k เพื่อไปยังจุดเริ่มต้นของวัฏจักรและทำให้ระยะห่างจากหัวถึง cyclestart และการชนกับ cyclestart เหมือนกัน
ดังนั้นตอนนี้หลังจากการชนครั้งแรกย้าย T กลับไปที่หัว T และ H จะพบกันที่ cyclestart ถ้าคุณเคลื่อนที่ด้วยอัตรา 1 ต่อ (ในขั้นตอน k สำหรับทั้งสอง)
ซึ่งหมายความว่าอัลกอริทึมคือ:
// ดูแลเคสเมื่อ k = 0 หรือ T และ H พบกันที่หัวของลูปโดยการคำนวณความยาวของลูป
- นับความยาวของวงจรโดยการย้าย T หรือ H รอบมันด้วยเคาน์เตอร์
- ย้ายตัวชี้ T2 ไปยังส่วนหัวของรายการ
- ย้ายตัวชี้ความยาวของขั้นตอนรอบ
- ย้ายตัวชี้ H2 อื่นไปที่หัว
- เคลื่อนย้าย T2 และ H2 ไปพร้อมกันจนกว่าจะถึงจุดเริ่มต้นของรอบ
แค่นั้นแหละ!
มีคำตอบมากมายเกี่ยวกับสิ่งนี้ แต่ฉันเคยมีไดอะแกรมสำหรับสิ่งนี้ซึ่งทำให้ฉันเห็นได้ง่ายกว่า อาจช่วยคนอื่นได้
ช่วงเวลา aha หลักสำหรับฉันคือ:
ฉันรู้ว่ามีคำตอบที่ยอมรับแล้วสำหรับปัญหานี้ แต่ฉันยังคงพยายามตอบอย่างคล่องแคล่ว สมมติ :
The length of the Path is 'X+B' where 'B' is the length of the looped path and X of the non looped path.
Speed of tortoise : v
Speed of hare : 2*v
Point where both meet is at a distance 'x + b - k' from the starting point.
ตอนนี้ให้กระต่ายและเต่าพบกันหลังจากเวลา 't' ตั้งแต่ต้น
ข้อสังเกต:
ถ้าระยะทางเดินทางโดยเต่า = v * t = x + (bk) (พูด)
จากนั้นระยะทางที่เดินทางโดยกระต่าย = 2 * v * t = x + (b - k) + b (เนื่องจากกระต่ายได้สำรวจส่วนที่วนลูปไว้แล้ว)
ขณะนี้มีเวลาการประชุมเหมือนกัน
=> x + 2 * b - k = 2 * (x + b - k)
=> x = k
หลักสูตรนี้หมายความว่าความยาวของเส้นทางที่ไม่ได้วนซ้ำนั้นเท่ากับระยะทางของจุดเริ่มต้นของลูปจากจุดที่ทั้งคู่พบกัน
เป็นเรื่องง่ายที่จะพิสูจน์ว่าพวกเขาทั้งคู่จะพบกันที่จุดเริ่มต้นหากคุณพิจารณาคณิตศาสตร์หลังจุดนัดพบ
ก่อนอื่นให้mแทนจุดเริ่มต้นของวงจรในรายการที่เชื่อมโยงและnแทนความยาวของรอบ จากนั้นสำหรับกระต่ายและเต่าที่จะพบกันเรามี:
( 2*t - m )%n = (t - m) %n, where t = time (at t = 0 , both are at the start)
ระบุเพิ่มเติมในเชิงคณิตศาสตร์:
(2*t - m - (t - m) ) = 0 modulo n , which implies , t = 0 modulo n
ดังนั้นพวกเขาจะได้พบกันในเวลาtซึ่งควรจะมีความยาวหลายรอบ ซึ่งหมายความว่าพวกเขาพบกันในสถานที่ซึ่งเป็น
(t-m) modulo n = (0-m) modulo n = (-m) modulo n
ซึ่งหมายความว่าพวกเขาพบกันที่สถานที่ซึ่งเป็น
ตอนนี้กลับมาที่คำถามถ้าคุณย้ายตัวชี้หนึ่งจากจุดเริ่มต้นของรายการที่เชื่อมโยงและอีกจุดจากจุดตัดหลังจากขั้นตอน m เราจะมีกระต่าย (ซึ่งเคลื่อนที่ภายในวงรอบ) มาถึงจุดที่เป็น((-m) + m) modulo n = 0 modulo n
ซึ่งไม่มีอะไรนอกจากเป็นจุดเริ่มต้นของวัฏจักรดังนั้นเราจะเห็นว่าหลังจากขั้นตอนม. มันมาถึงจุดเริ่มต้นของวงจรและเต่าจะพบมันที่นั่นตามที่จะสำรวจม. ขั้นตอนจากจุดเริ่มต้นของรายการที่เชื่อมโยง
ตามบันทึกข้างเรายังสามารถคำนวณเวลาของสี่แยกของพวกเขาในลักษณะนี้: สภาพt = 0 modulo n
บอกเราว่าพวกเขาจะได้พบกับช่วงเวลาที่มีหลายความยาวรอบและยังเสื้อควรมากกว่าเมตรที่พวกเขาจะได้พบใน วงจร ดังนั้นเวลาที่ถ่ายจะเท่ากับหลายแรกของnซึ่งมากกว่าเมตร
สมมติว่าพอยน์เตอร์ของคุณพบกันที่จุดตัดของ y และ z
n และ m คือจำนวนของลูปที่เร็วขึ้นและตัวชี้ที่ช้าจะใช้เวลาตามลำดับก่อนการประชุม
อ้างถึงภาพสำหรับส่วนที่เหลือของการพิสูจน์ ค้นหาจุดเริ่มต้นของลูปในรายการที่เชื่อมโยง