Một hàm đệ quy có thể lặp lại / vòng lặp không?


12

Tôi đã nghiên cứu về các hàm đệ quy và rõ ràng, chúng là các hàm tự gọi chúng và không sử dụng các vòng lặp / vòng lặp (nếu không nó sẽ không phải là một hàm đệ quy).

Tuy nhiên, trong khi lướt web để lấy ví dụ (vấn đề đệ quy 8 lần), tôi đã tìm thấy chức năng này:

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;
}

Có một whilevòng lặp liên quan.

... Vì vậy, bây giờ tôi có một chút mất mát. Tôi có thể sử dụng các vòng lặp hay không?


5
Liệu nó biên dịch. Đúng. Vậy tại sao lại hỏi?
Thomas Eding

6
Các toàn bộ định nghĩa đệ quy là tại một số điểm, chức năng có thể tái gọi là phần thực hiện riêng của mình trước khi nó trả (cho dù nó lại gọi bởi chính nó hoặc bởi một số chức năng khác nó gọi). Không có gì về định nghĩa đó loại trừ khả năng lặp.
cHao

Là một phụ lục cho nhận xét của cHao, một hàm đệ quy sẽ được gọi lại trên một phiên bản dễ dàng hơn của chính nó (nếu không, nó sẽ lặp lại mãi mãi). Để trích dẫn orble (từ tiếng Anh đơn giản, đệ quy là gì? ): "Lập trình đệ quy là quá trình giảm dần một vấn đề để dễ dàng giải quyết các phiên bản của chính nó." Trong trường hợp này, phiên bản khó nhất placeQueenlà "đặt 8 nữ hoàng" và phiên bản dễ hơn placeQueenlà "địa điểm 7 nữ hoàng" (sau đó đặt 6, v.v.)
Brian

Bạn có thể sử dụng bất cứ thứ gì có tác dụng Omega. Rất hiếm khi các đặc tả phần mềm chỉ định phong cách lập trình sẽ sử dụng - cho dù bạn đang ở trường và bài tập của bạn nói như vậy.
Apoorv Khurasia

@ThomasEding: Vâng, tất nhiên nó biên dịch và hoạt động. Nhưng tôi chỉ đang học ngành kỹ thuật tại thời điểm này - Điều quan trọng đối với tôi, tại thời điểm này, là khái niệm / định nghĩa nghiêm ngặt và không phải là cách các lập trình viên sử dụng nó ngày nay. Vì vậy, tôi đang hỏi liệu khái niệm tôi có có đúng không (có vẻ như không phải vậy).
Omega

Câu trả lời:


41

Bạn đã hiểu nhầm đệ quy: mặc dù nó có thể được sử dụng để thay thế phép lặp, nhưng hoàn toàn không có yêu cầu nào cho hàm đệ quy không có các lần lặp bên trong chính nó.

Yêu cầu duy nhất cho một hàm được xem là đệ quy là sự tồn tại của một đường dẫn mã mà qua đó nó gọi chính nó, trực tiếp hoặc gián tiếp. Tất cả các hàm đệ quy đúng cũng có một điều kiện nào đó, ngăn không cho chúng "đệ quy xuống" mãi mãi.

Hàm đệ quy của bạn là lý tưởng để minh họa cấu trúc của tìm kiếm đệ quy với quay lui. Nó bắt đầu bằng việc kiểm tra điều kiện thoát row < nvà tiến hành đưa ra quyết định tìm kiếm về mức độ đệ quy của nó (tức là chọn một vị trí có thể cho số nữ hoàng row). Sau mỗi lần lặp, một cuộc gọi đệ quy được thực hiện để xây dựng theo cấu hình mà hàm đã tìm thấy cho đến nay; cuối cùng, nó " rowchạm đáy" khi đạt đến ncuộc gọi đệ quy có nmức độ sâu.


1
+1 cho các hàm đệ quy "chính xác" có rất nhiều điều kiện không chính xác ngoài đó không phải là heh
Jimmy Hoffa

6
+1 "tái diễn" mãi mãi `Rùa () {Rùa ();}
Mr.Mindor

1
@ Mr.Mindor Tôi thích câu trích dẫn "Đó là rùa xuống" :)
dasblinkenlight

Điều đó làm tôi mỉm cười :-)
Martijn Verburg

2
"Tất cả các hàm đệ quy đúng cũng có một điều kiện nào đó, ngăn chúng" đệ quy xuống "mãi mãi." là không đúng với đánh giá không nghiêm ngặt.
Pubby

12

Cấu trúc chung của hàm đệ quy là như thế này:

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

Các văn bản mà tôi đánh dấu là /*recursive processing*/có thể là bất cứ điều gì. Nó có thể bao gồm một vòng lặp, nếu vấn đề đang được giải quyết đòi hỏi nó, và cũng có thể bao gồm các cuộc gọi đệ quy đến myRecursiveFunction.


1
Điều đó gây hiểu lầm, bởi vì nó ngụ ý rằng chỉ có một cuộc gọi đệ quy và gần như không bao gồm các trường hợp trong đó cuộc gọi đệ quy nằm trong một vòng lặp (ví dụ: truyền qua cây B).
Peter Taylor

@PeterTaylor: Vâng, tôi đã cố gắng làm cho nó đơn giản.
Thất vọngWithFormsDesigner

Hoặc thậm chí nhiều cuộc gọi không có vòng lặp, như đi ngang qua cây nhị phân đơn giản, trong đó bạn có 2 cuộc gọi do mỗi nút có 2 con.
Izkata

6

Bạn chắc chắn có thể sử dụng các vòng lặp trong một hàm đệ quy. Điều làm cho một hàm đệ quy chỉ là thực tế là hàm gọi chính nó tại một số điểm trong đường dẫn thực thi của nó. Tuy nhiên, bạn nên có một số điều kiện để ngăn chặn các cuộc gọi đệ quy vô hạn mà chức năng của bạn không thể trả về.


1

Các cuộc gọi và vòng lặp đệ quy chỉ là hai cách / cấu trúc để thực hiện tính toán lặp.

Một whilevòng lặp tương ứng với một cuộc gọi đệ quy đuôi (xem ví dụ ở đây ), tức là một lần lặp mà bạn không cần lưu kết quả trung gian giữa hai lần lặp (tất cả các kết quả của một chu kỳ đã sẵn sàng khi bạn bước vào chu kỳ tiếp theo). Nếu bạn cần lưu trữ các kết quả trung gian mà bạn có thể sử dụng lại sau này, bạn có thể sử dụng một whilevòng lặp cùng với một ngăn xếp (xem tại đây ) hoặc một cuộc gọi đệ quy không theo đuôi (tức là tùy ý).

Nhiều ngôn ngữ cho phép bạn sử dụng cả hai cơ chế và bạn có thể chọn một cơ chế phù hợp hơn với mình và thậm chí trộn chúng lại với nhau trong mã của bạn. Trong các ngôn ngữ bắt buộc như C, C ++, Java, v.v. bạn thường sử dụng một whilehoặc forvòng lặp khi bạn không cần ngăn xếp và bạn sử dụng các cuộc gọi đệ quy khi bạn cần một ngăn xếp (bạn hoàn toàn sử dụng ngăn xếp thời gian chạy). Haskell (một ngôn ngữ chức năng) không cung cấp cấu trúc điều khiển lặp để bạn chỉ có thể sử dụng các cuộc gọi đệ quy để thực hiện phép lặp.

Trong ví dụ của bạn (xem ý kiến ​​của tôi):

// 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

Bạn có quyền nghĩ rằng có một mối quan hệ giữa đệ quy và lặp hoặc lặp. Các thuật toán đệ quy thường được chuyển đổi thủ công hoặc thậm chí tự động thành các giải pháp lặp bằng cách sử dụng tối ưu hóa cuộc gọi đuôi.

Trong tám kiến ​​trúc, phần đệ quy có liên quan đến việc lưu trữ dữ liệu cần thiết để theo dõi ngược. Khi bạn nghĩ về đệ quy, sẽ rất có giá trị khi nghĩ về những gì được đẩy trên ngăn xếp. Ngăn xếp có thể chứa các tham số giá trị và các biến cục bộ đóng vai trò chính trong thuật toán hoặc đôi khi các công cụ không liên quan rõ ràng như địa chỉ trả về hoặc trong trường hợp này, một giá trị được truyền với số lượng kiến ​​trúc được sử dụng nhưng không thay đổi bởi thuật toán.

Hành động xảy ra ở tám nữ hoàng là về cơ bản chúng tôi được cung cấp một giải pháp một phần cho một số nữ hoàng trong một số cột đầu tiên mà từ đó chúng tôi xác định lặp lại các lựa chọn hợp lệ trong cột hiện tại mà chúng tôi chuyển qua đệ quy để được đánh giá cho các cột còn lại. Tại địa phương, tám nữ hoàng theo dõi hàng nào đang thử và nếu theo dõi ngược xảy ra, họ sẵn sàng bước qua các hàng còn lại hoặc theo dõi thêm bằng cách quay lại nếu không tìm thấy hàng nào khác có thể hoạt động.


0

Phần "tạo một phiên bản nhỏ hơn của vấn đề" có thể có các vòng lặp. Miễn là phương thức gọi chính nó là một tham số phiên bản nhỏ hơn của vấn đề, phương thức được đệ quy. Tất nhiên, một điều kiện thoát, khi phiên bản nhỏ nhất có thể của vấn đề được giải quyết và phương thức trả về một giá trị, phải được cung cấp để tránh điều kiện tràn ngăn xếp.

Phương pháp trong câu hỏi của bạn là đệ quy.


0

Recursion về cơ bản gọi lại chức năng của bạn và ưu điểm chính của đệ quy là tiết kiệm bộ nhớ. Đệ quy có thể có các vòng lặp trong đó chúng được sử dụng để thực hiện một số thao tác khác.


Bắt đầu khác nhau. Nhiều thuật toán có thể là đệ quy hoặc lặp và giải pháp đệ quy thường tốn nhiều bộ nhớ hơn nếu bạn đếm địa chỉ trả về, tham số và biến cục bộ phải được đẩy trên ngăn xếp. Một số ngôn ngữ phát hiện và giúp tối ưu hóa cho đệ quy đuôi hoặc tối ưu hóa cuộc gọi đuôi, nhưng đôi khi đây là ngôn ngữ hoặc mã cụ thể.
DeveloperDon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.