สิ่งที่ควรพิจารณาเพื่อพิจารณาว่าคุณสามารถใช้การเรียกซ้ำเพื่อแก้ไขปัญหาได้หรือไม่


10

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

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


ฉันมีความคิดบางอย่าง:

หากเราใช้การเรียกซ้ำข้อมูลซึ่งลดลงครึ่งหนึ่งทุกครั้งดูเหมือนว่าไม่มีปัญหาในการเรียกซ้ำเนื่องจากข้อมูลทั้งหมดที่สามารถพอดีกับ RAM ขนาด 16GB หรือแม้กระทั่งฮาร์ดไดรฟ์ 8TB สามารถจัดการได้โดยการเรียกซ้ำเพียง 42 ระดับ (ดังนั้นจึงไม่มีการล้นสแต็ค (ฉันคิดว่าในสภาพแวดล้อมบางอย่างสแต็คอาจมีความลึก 4000 ระดับทางมากกว่า 42 แต่ในขณะเดียวกันก็ขึ้นอยู่กับว่าคุณมีตัวแปรเฉพาะที่คุณมีเท่าไรในแต่ละสแตกการโทร หากมีตัวแปรโลคัลจำนวนมากและเป็นขนาดหน่วยความจำไม่ใช่ระดับซึ่งกำหนดสแต็กโอเวอร์โฟลว์))

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

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

คำถามสุดท้ายคือสิ่งที่ต้องพิจารณาก่อนที่คุณจะตัดสินใจใช้การเรียกซ้ำเพื่อแก้ไขปัญหา


7
มันค่อนข้างง่ายจริง ๆ : "เป็นวิธีแก้ปัญหาเล็กน้อยหรือไม่ถ้าฉันสามารถสันนิษฐานได้ว่ารู้วิธีแก้ปัญหาที่เล็กน้อยกว่านี้หรือไม่?"
Kilian Foth

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

คุณอาจพบว่ามีประโยชน์ - stackoverflow.com/questions/3021/…
Kishor Kundan

คำตอบ:


15

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

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

โปรดทราบว่าโซลูชันที่เรียกซ้ำ (ถูกต้อง) สามารถเปลี่ยนเป็นโซลูชันที่ไม่เรียกซ้ำได้ดังนั้นคุณไม่จำเป็นต้องเลือกอย่างหนักระหว่างสองวิธีนี้

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


1 - รวมถึงข้อควรพิจารณาเช่นว่ากลุ่มเป้าหมาย ... และนี่อาจรวมถึงโปรแกรมเมอร์ที่อ่านรหัสที่ใช้งานได้จริง ... จะดูวิธีการแก้ปัญหาแบบหนึ่งว่า "ดูเป็นธรรมชาติ" มากกว่าอีกวิธีหนึ่ง ความคิดของ "ธรรมชาติ" จะแตกต่างกันไปในแต่ละบุคคลขึ้นอยู่กับว่าพวกเขาเรียนรู้การเขียนโปรแกรมหรืออัลกอริทึม (ฉันขอท้าให้ใครก็ตามที่เสนอ "ความเป็นธรรมชาติ" เป็นเกณฑ์เบื้องต้นในการตัดสินใจใช้การเรียกซ้ำ (หรือไม่) เพื่อกำหนด "ความเป็นธรรมชาติ" ในแง่ของวัตถุประสงค์; เช่นคุณจะวัดอย่างไร)


2
ปัญหาบางอย่างแสดงออกได้อย่างเป็นธรรมชาติมากขึ้นโดยใช้การเรียกซ้ำ Tree traversal เป็นต้น
Frank Hileman

อัปเดตคำตอบของฉันเพื่อแก้ไขประเด็นนี้
สตีเฟนซี

1
ด้วยความคำนึงถึง "ความเป็นธรรมชาติ": การแวะผ่านต้นไม้โดยไม่มีการเรียกซ้ำตัวอย่างเช่นมีแนวโน้มที่จะสร้างรหัสวัตถุประสงค์ทั่วไปที่ใหญ่กว่าและน้อยกว่า ลองพิจารณาตัวอย่างเช่นการใช้การเรียก polymorphic เพื่อสำรวจต้นไม้โดยมีพฤติกรรมที่แตกต่างกันสำหรับโหนดใบไม้และคอมโพสิต สิ่งนี้เป็นไปไม่ได้หากไม่มีการเรียกซ้ำ
Frank Hileman

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

อย่างไรก็ตามภาษาการเขียนโปรแกรมแรกที่ฉันได้เรียนรู้ (FORTRAN 4) ไม่สนับสนุนการเรียกซ้ำทั้งหมด
สตีเฟ่นซี

1

ในฐานะโปรแกรมเมอร์ C / C ++ สิ่งที่ต้องพิจารณาอันดับแรกของฉันคือประสิทธิภาพ กระบวนการตัดสินใจของฉันเป็นอย่างไร:

  1. ความลึกสูงสุดของ call stack คืออะไร? หากลึกเกินไปให้กำจัดการสอบถามซ้ำ ถ้าตื้นไปที่ 2

  2. ฟังก์ชั่นนี้น่าจะเป็นปัญหาคอขวดของโปรแกรมของฉันหรือไม่? ถ้าใช่ไปที่ 3 หากไม่มีให้ทำการสอบถามซ้ำ ถ้าไม่แน่ใจให้เรียกใช้ตัวสร้างโปรไฟล์

  3. เศษส่วนของเวลา CPU ที่ใช้ในการเรียกฟังก์ชันแบบเรียกซ้ำคืออะไร? หากการเรียกใช้ฟังก์ชันใช้เวลาน้อยกว่าร่างกายส่วนที่เหลืออย่างเห็นได้ชัดก็ถือว่าใช้การเรียกซ้ำได้


0

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

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

เมื่อเขียนฟังก์ชั่นใน C ++ ฉันพบว่าตัวเองถกเถียงกันก่อนที่จะใช้ฟังก์ชั่นวนซ้ำ คำถามที่ฉันถามตัวเองคือ:

  • การคำนวณสามารถทำได้โดยใช้อัลกอริทึมซ้ำได้หรือไม่? ถ้าใช่ใช้วิธีการวนซ้ำ

  • ความลึกของการเรียกซ้ำสามารถเพิ่มขึ้นตามขนาดของโมเดลได้หรือไม่? ฉันเพิ่งวิ่งเข้าไปในกรณีที่ความลึกของการเรียกซ้ำเพิ่มขึ้นเกือบ 13,000 เนื่องจากขนาดของโมเดล ฉันต้องแปลงฟังก์ชั่นเพื่อใช้อัลกอริทึมซ้ำการโพสต์รีบเร่ง

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

  • ฟังก์ชั่นสามารถซับซ้อนเกินไปโดยใช้อัลกอริทึมซ้ำได้หรือไม่? ถ้าใช่ใช้ฟังก์ชันเรียกซ้ำ ฉันไม่ได้ลองเขียนqsortโดยใช้วิธีวนซ้ำ แต่ฉันรู้สึกว่าการใช้ฟังก์ชั่นวนซ้ำนั้นเป็นเรื่องที่เป็นธรรมชาติมากขึ้น


0

สำหรับตัวเลขฟีโบนักชีการ "เรียกซ้ำ" ที่ไร้เดียงสานั้นโง่มาก ๆ นั่นเป็นเพราะมันนำไปสู่ปัญหาย่อยเดียวกันที่ถูกแก้ไขซ้ำแล้วซ้ำอีก

ที่จริงแล้วมีการแปรผันเล็กน้อยของตัวเลขฟีโบนักชีซึ่งการเรียกซ้ำนั้นมีประสิทธิภาพมาก: เมื่อพิจารณาจากจำนวน n ≥ 1 ให้คำนวณทั้งฟิล (n) และ Fib (n-1) ดังนั้นคุณต้องการฟังก์ชันที่ส่งคืนผลลัพธ์สองรายการให้เรียกฟังก์ชันนี้ว่า fib2

การใช้งานค่อนข้างง่าย:

function fib2 (n) -> (fibn, fibnm1) {
    if n ≤ 1 { return (1, 1) }
    let (fibn, fibnm1) = fib2 (n-1)
    return (fibn + fibnm1, fibn)
}

คุณคิดว่าคุณสามารถเขียนโปรแกรมในภาษาทั่วไปได้หรือไม่? และคุณfib2คืนค่าตัวเลขและคุณfib2()ไม่พอดีกับอินเทอร์เฟซของfib()ซึ่งก็คือให้ตัวเลขส่งคืนตัวเลข ดูเหมือนว่าคุณfib(n)จะกลับมาfib2(n)[0]แต่โปรดเจาะจง
nonopolarity
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.