ฟังก์ชั่นวนซ้ำสามารถมีการวนซ้ำ / ลูปได้หรือไม่?


12

ฉันกำลังศึกษาเกี่ยวกับฟังก์ชั่นวนซ้ำและเห็นได้ชัดว่าพวกมันเป็นฟังก์ชันที่เรียกตัวเองและไม่ใช้การวนซ้ำ / วนซ้ำ

อย่างไรก็ตามในขณะที่ท่องเว็บเพื่อดูตัวอย่าง (ปัญหา 8-queens-recursive) ฉันพบฟังก์ชันนี้:

private boolean placeQueen(int rows, int queens, int n) {
    boolean result = false;
    if (row < n) {
        while ((queens[row] < n - 1) && !result) {
            queens[row]++;
            if (verify(row,queens,n)) {
                ok = placeQueen(row + 1,queens,n);
            }
        }
        if (!result) {
            queens[row] = -1;
        }
    }else{
        result = true;
    }
    return result;
}

มีการwhileวนซ้ำที่เกี่ยวข้อง

... ดังนั้นตอนนี้ฉันหลงทาง ฉันสามารถใช้ลูปได้หรือไม่?


5
มันรวบรวม ใช่. เหตุใดจึงต้องถาม
โทมัส Eding

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

ในฐานะที่เป็นภาคผนวกของความคิดเห็นของ cHao ฟังก์ชั่นวนซ้ำจะเรียกอีกครั้งในเวอร์ชันที่ง่ายกว่าของตัวเอง (ไม่เช่นนั้นมันจะวนซ้ำตลอดไป) เพื่ออ้างถึง orbling (จากภาษาอังกฤษแบบธรรมดาการเรียกซ้ำคืออะไร ): "การเขียนโปรแกรมแบบเรียกซ้ำเป็นกระบวนการของการลดปัญหาอย่างต่อเนื่องเพื่อแก้ไขปัญหาของตัวเองได้ง่ายขึ้น" ในกรณีนี้รุ่นที่ยากที่สุดplaceQueenคือ "place 8 queens" และรุ่นที่ง่ายกว่าplaceQueenคือ "place 7 queens" (จากนั้นใส่ 6 เป็นต้น)
Brian

คุณสามารถใช้ทุกอย่างที่ทำงานได้โอเมก้า ข้อกำหนดซอฟต์แวร์ที่ไม่ค่อยทำจะระบุรูปแบบของการเขียนโปรแกรมที่จะใช้ - หากคุณไม่ได้อยู่ในโรงเรียน
Apoorv Khurasia

@ThomasEding: ใช่แน่นอนมันรวบรวมและใช้งานได้ แต่ตอนนี้ฉันกำลังเรียนวิศวกรรมอยู่ - สิ่งที่สำคัญสำหรับฉันในตอนนี้คือแนวคิด / คำจำกัดความที่เข้มงวดไม่ใช่วิธีที่โปรแกรมเมอร์ใช้ในปัจจุบัน ดังนั้นฉันจึงถามว่าแนวคิดที่ฉันมีนั้นถูกต้องหรือไม่
โอเมก้า

คำตอบ:


41

คุณเข้าใจผิดเกี่ยวกับการเรียกซ้ำ: แม้ว่ามันสามารถใช้เพื่อแทนที่การวนซ้ำได้ แต่ก็ไม่มีข้อกำหนดใด ๆ สำหรับฟังก์ชันการเรียกซ้ำที่ไม่ให้มีการวนซ้ำภายในตัวมันเอง

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

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


1
+1 สำหรับฟังก์ชั่นการเรียกซ้ำ "ถูกต้อง" นั้นมีเงื่อนไขและมีจำนวนที่ไม่ถูกต้องมากมายซึ่งไม่น่าแปลกใจ
Jimmy Hoffa

6
+1 "ซ้ำลง" ตลอดกาล `Turtle () {Turtle ();}
Mr.Mindor

1
@ Mr.Mindor ฉันรัก "มันของเต่าทุกทางลง" อ้าง :)
dasblinkenlight

นั่นทำให้ฉันยิ้ม :-)
Martijn Verburg

2
"ฟังก์ชั่นเรียกซ้ำที่ถูกต้องทั้งหมดยังมีเงื่อนไขบางอย่างทำให้ไม่สามารถ" เรียกซ้ำ "ได้ตลอดไป ไม่เป็นความจริงกับการประเมินที่ไม่เข้มงวด
Pubby

12

โครงสร้างทั่วไปของฟังก์ชันแบบเรียกซ้ำเป็นดังนี้:

myRecursiveFunction(inputValue)
begin
   if evaluateBaseCaseCondition(inputValue)=true then
       return baseCaseValue;
   else
       /*
       Recursive processing
       */
       recursiveResult = myRecursiveFunction(nextRecursiveValue); //nextRecursiveValue could be as simple as inputValue-1
       return recursiveResult;
   end if
end

ข้อความที่ฉันทำเครื่องหมายว่า/*recursive processing*/อาจเป็นอะไรก็ได้ มันอาจmyRecursiveFunctionรวมถึงห่วงหากปัญหาถูกแก้ไขต้องใช้มันและยังอาจรวมถึงการโทรเรียกซ้ำไป


1
นั่นเป็นสิ่งที่ทำให้เข้าใจผิดเพราะมันหมายความว่ามีการโทรซ้ำเพียงครั้งเดียวและไม่รวมกรณีที่การโทรแบบเรียกซ้ำอยู่ภายในลูป (เช่น B-tree traversal)
Peter Taylor

@PeterTaylor: ใช่ฉันพยายามทำให้มันง่าย
FrustratedWithFormsDesigner

หรือแม้กระทั่งการโทรหลายครั้งโดยไม่มีการวนซ้ำเช่นการข้ามต้นไม้ไบนารีแบบธรรมดาที่คุณมีการโทร 2 ครั้งเนื่องจากแต่ละโหนดมีลูก 2 ลูก
Izkata

6

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


1

การเรียกซ้ำและการวนซ้ำเป็นเพียงสองวิธี / โครงสร้างเพื่อใช้การคำนวณแบบวนซ้ำ

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

หลายภาษาให้คุณใช้ทั้งสองกลไกและคุณสามารถเลือกภาษาที่เหมาะกับคุณมากยิ่งขึ้นและแม้แต่ผสมเข้าด้วยกันในรหัสของคุณ ในภาษาที่จำเป็นเช่น C, C ++, Java เป็นต้นโดยปกติแล้วคุณจะใช้ a whileหรือforloop เมื่อคุณไม่ต้องการ stack และคุณใช้การเรียกซ้ำเมื่อคุณต้องการ stack (โดยปริยายใช้ stack โดยใช้ run-time) Haskell (ภาษาที่ใช้งานได้) ไม่มีโครงสร้างการควบคุมการทำซ้ำดังนั้นคุณจึงสามารถใช้การเรียกซ้ำเพื่อดำเนินการซ้ำได้

ในตัวอย่างของคุณ (ดูความคิดเห็นของฉัน):

// queens should have type int [] , not int.
private boolean placeQueen(int row, int [] queens, int n)
{
    boolean result = false;
    if (row < n)
    {
        // Iterate with queens[row] = 1 to n - 1.
        // After each iteration, you either have a result
        // in queens, or you have to try the next column for
        // the current row: no intermediate result.
        while ((queens[row] < n - 1) && !result)
        {
            queens[row]++;
            if (verify(row,queens,n))
            {
                // I think you have 'result' here, not 'ok'.
                // This is another loop (iterate on row).
                // The loop is implemented as a recursive call
                // and the previous values of row are stored on
                // the stack so that we can resume with the previous
                // value if the current attempt finds no solution.
                result = placeQueen(row + 1,queens,n);
            }
        }
        if (!result) {
            queens[row] = -1;
        }
    }else{
        result = true;
    }
    return result;
}

1

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

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

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


0

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

วิธีการในคำถามของคุณซ้ำ


0

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


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