5

Trích dẫn từ Code Complete 2,Làm thế nào để đệ quy làm cho việc sử dụng bộ nhớ thời gian chạy không thể đoán trước?

int Factorial(int number) { 
    if (number == 1) { 
     return 1; 
    } 
    else { 
     return number * Factorial(number - 1); 
    } 
} 

Ngoài việc là chậm[1] và làm việc sử dụng bộ nhớ thời gian chạy không thể đoán trước[2], phiên bản đệ quy của thói quen này khó khăn hơn để hiểu hơn phiên bản lặp lại, như sau:

int Factorial(int number) { 
    int intermediateResult = 1; 
    for (int factor = 2; factor <= number; factor++) { 
     intermediateResult = intermediateResult * factor; 
    } 
    return intermediateResult; 
} 

Tôi nghĩ phần chậm là do chi phí cuộc gọi hàm không cần thiết.

Nhưng làm cách nào để đệ quy sử dụng bộ nhớ thời gian chạy không thể đoán trước?

Chúng ta không thể dự đoán bao nhiêu bộ nhớ sẽ cần thiết (như chúng ta biết khi nào đệ quy được cho là kết thúc)? Tôi nghĩ rằng nó sẽ không thể dự đoán được như trường hợp lặp lại, nhưng không phải như vậy nữa.

+1

Lưu ý rằng đệ quy CÓ THỂ chạy trong thời gian không đổi NẾU quy trình sử dụng đệ quy đuôi VÀ nếu ngôn ngữ hỗ trợ tối ưu hóa cuộc gọi đuôi. Tra cứu "đệ quy đuôi" nếu bạn quan tâm. – erjiang

+0

Ngoài ra, trên một số CPU như phí gọi hàm Sparc là rất thấp. Và với một số ngôn ngữ lập trình như Tcl, hàm gọi thực sự nhanh hơn mã nội tuyến (do cách hoạt động của bytecompiler). – slebetman

Trả lời

3

Bởi vì các phương pháp đệ quy thực tế gọi chúng là bản thân nhiều lần, rất cần nhiều bộ nhớ ngăn xếp. Kể từ khi ngăn xếp được giới hạn, lỗi sẽ xảy ra nếu ngăn xếp ngăn xếp được vượt quá.

+1

thats những gì tôi nghĩ, nhưng tại sao điều đó sẽ không thể đoán trước? – Lazer

+3

Tôi nghĩ rằng nó chỉ có nghĩa là "phụ thuộc vào một giá trị tại thời gian chạy". Phiên bản lặp lại tôi có thể dự đoán luôn luôn mất 8 byte của stack (hoặc bất cứ điều gì nó cần), trong khi tôi không thể "dự đoán" một đệ quy, vì nó phụ thuộc vào giá trị của 'số' thông qua trong thời gian chạy. Vì vậy, "không thể đoán trước" có nghĩa là "không thể dự đoán một giá trị không đổi tại thời gian biên dịch". – Brian

+0

Nó sẽ không thể dự đoán được nếu bạn không biết bao nhiêu lần hàm gọi chính nó, đúng không? Nếu hàm này tự gọi quá nhiều lần, bạn sẽ có một 'stackoverflow' =) – gideon

0

Nếu mức đệ quy trở nên quá sâu, bạn sẽ thổi ngăn xếp cuộc gọi và ăn nhiều bộ nhớ trong quá trình. Điều này có thể xảy ra nếu number của bạn là giá trị "đủ lớn". Bạn có thể làm tồi tệ hơn điều này? Có, nếu chức năng của bạn phân bổ nhiều đối tượng hơn với mọi cuộc gọi đệ quy.

1

Chúng ta không thể dự đoán được bao nhiêu bộ nhớ cần thiết (như chúng ta biết khi nào đệ quy được cho là kết thúc)? Tôi nghĩ rằng nó sẽ không thể dự đoán được như trường hợp lặp lại, nhưng không phải như vậy nữa.

Không, không phải trong trường hợp chung. Xem thảo luận về halting problem để biết thêm thông tin cơ bản. Bây giờ, đây là phiên bản đệ quy của một trong những vấn đề được đăng ở đó:

void u(int x) { 
    if (x != 1) { 
     u((x % 2 == 0) ? x/2 : 3*x+1); 
    } 
} 

Nó thậm chí còn có tính đệ quy đuôi. Vì bạn không thể dự đoán nếu điều này thậm chí sẽ chấm dứt bình thường, làm thế nào bạn có thể dự đoán bao nhiêu bộ nhớ là cần thiết?