Đệ quy là một chủ đề khó hiểu và tôi không nghĩ rằng tôi hoàn toàn có thể thực hiện công lý ở đây. Thay vào đó, tôi sẽ cố gắng tập trung vào đoạn mã cụ thể mà bạn có ở đây và cố gắng mô tả cả trực giác về lý do giải pháp hoạt động và cơ chế về cách mã tính toán kết quả của nó.
Mã bạn đã đưa ra ở đây giải quyết vấn đề sau: bạn muốn biết tổng của tất cả các số nguyên từ a đến b, bao gồm. Ví dụ của bạn, bạn muốn tổng các số từ 2 đến 5, bao gồm, đó là
2 + 3 + 4 + 5
Khi cố gắng giải quyết vấn đề một cách đệ quy, một trong những bước đầu tiên là tìm ra cách chia nhỏ vấn đề thành một vấn đề nhỏ hơn với cùng cấu trúc. Vì vậy, giả sử rằng bạn muốn tổng hợp các số từ 2 đến 5, bao gồm. Một cách để đơn giản hóa điều này là lưu ý rằng tổng trên có thể được viết lại thành
2 + (3 + 4 + 5)
Ở đây, (3 + 4 + 5) là tổng của tất cả các số nguyên nằm trong khoảng từ 3 đến 5. Nói cách khác, nếu bạn muốn biết tổng của tất cả các số nguyên từ 2 đến 5, hãy bắt đầu bằng cách tính tổng của tất cả các số nguyên trong khoảng từ 3 đến 5, sau đó thêm 2.
Vậy làm thế nào để bạn tính tổng của tất cả các số nguyên từ 3 đến 5, bao gồm? Chà, tổng đó là
3 + 4 + 5
thay vào đó có thể được coi là
3 + (4 + 5)
Ở đây, (4 + 5) là tổng của tất cả các số nguyên từ 4 đến 5, bao gồm. Vì vậy, nếu bạn muốn tính tổng của tất cả các số từ 3 đến 5, bao gồm, bạn sẽ tính tổng của tất cả các số nguyên từ 4 đến 5, sau đó thêm 3.
Có một mô hình ở đây! Nếu bạn muốn tính tổng các số nguyên giữa a và b, đã bao gồm, bạn có thể làm như sau. Đầu tiên, tính tổng các số nguyên giữa a + 1 và b, đã bao gồm. Tiếp theo, thêm một vào tổng số đó. Bạn sẽ nhận thấy rằng "tính tổng các số nguyên giữa a + 1 và b, bao gồm" xảy ra khá giống với loại vấn đề chúng tôi đã cố gắng giải quyết, nhưng với các tham số hơi khác nhau. Thay vì tính toán từ a đến b, bao gồm, chúng tôi tính toán từ +1 đến b, bao gồm. Đó là bước đệ quy - để giải quyết vấn đề lớn hơn ("tổng từ a đến b, bao gồm"), chúng tôi giảm vấn đề thành phiên bản nhỏ hơn của chính nó ("tổng từ 1 đến 1, bao gồm.").
Nếu bạn xem mã bạn có ở trên, bạn sẽ nhận thấy rằng có bước này trong đó:
return a + sumInts(a + 1, b: b)
Mã này chỉ đơn giản là một bản dịch của logic trên - nếu bạn muốn tính tổng từ a đến b, bao gồm, hãy bắt đầu bằng cách tính tổng + 1 đến b, bao gồm (đó là cuộc gọi đệ quy đến sumInt
s), sau đó thêm a
.
Tất nhiên, tự nó phương pháp này sẽ không thực sự hiệu quả. Ví dụ, làm thế nào bạn sẽ tính tổng của tất cả các số nguyên từ 5 đến 5 bao gồm? Vâng, bằng cách sử dụng logic hiện tại của chúng tôi, bạn sẽ tính tổng của tất cả các số nguyên từ 6 đến 5, sau đó thêm 5. Vậy làm thế nào để bạn tính tổng của tất cả các số nguyên từ 6 đến 5, bao gồm? Chà, sử dụng logic hiện tại của chúng tôi, bạn sẽ tính tổng của tất cả các số nguyên từ 7 đến 5, bao gồm, sau đó thêm 6. Bạn sẽ nhận thấy một vấn đề ở đây - điều này cứ tiếp tục và tiếp tục!
Trong giải quyết vấn đề đệ quy, cần có một số cách để ngừng đơn giản hóa vấn đề và thay vào đó chỉ cần giải quyết trực tiếp. Thông thường, bạn sẽ tìm thấy một trường hợp đơn giản trong đó câu trả lời có thể được xác định ngay lập tức, sau đó cấu trúc giải pháp của bạn để giải quyết trực tiếp các trường hợp đơn giản khi chúng phát sinh. Điều này thường được gọi là trường hợp cơ sở hoặc cơ sở đệ quy .
Vì vậy, trường hợp cơ bản trong vấn đề cụ thể này là gì? Khi bạn tính tổng các số nguyên từ a đến b, bao gồm, nếu a xảy ra lớn hơn b, thì câu trả lời là 0 - không có bất kỳ số nào trong phạm vi! Do đó, chúng tôi sẽ cấu trúc giải pháp của chúng tôi như sau:
- Nếu a> b, thì câu trả lời là 0.
- Mặt khác (a ≤ b), nhận câu trả lời như sau:
- Tính tổng các số nguyên giữa a + 1 và b.
- Thêm một để có được câu trả lời.
Bây giờ, so sánh mã giả này với mã thực tế của bạn:
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a + 1, b: b)
}
}
Lưu ý rằng gần như chính xác một bản đồ một-một giữa giải pháp được nêu trong mã giả và mã thực tế này. Bước đầu tiên là trường hợp cơ sở - trong trường hợp bạn yêu cầu tổng một dãy số trống, bạn nhận được 0. Nếu không, hãy tính tổng giữa a + 1 và b, sau đó đi thêm a.
Cho đến nay, tôi chỉ đưa ra một ý tưởng cấp cao đằng sau mã. Nhưng bạn đã có hai câu hỏi khác, rất tốt. Đầu tiên, tại sao điều này không luôn luôn trả về 0, với điều kiện là hàm trả về 0 nếu a> b? Thứ hai, 14 thực sự đến từ đâu? Chúng ta hãy lần lượt xem xét những thứ này.
Hãy thử một trường hợp rất, rất đơn giản. Điều gì xảy ra nếu bạn gọi sumInts(6, 5)
? Trong trường hợp này, theo dõi mã, bạn thấy rằng hàm chỉ trả về 0. Đó là điều đúng đắn để làm - không có bất kỳ số nào trong phạm vi. Bây giờ, hãy thử một cái gì đó khó hơn. Điều gì xảy ra khi bạn gọi sumInts(5, 5)
? Vâng, đây là những gì xảy ra:
- Bạn gọi
sumInts(5, 5)
. Chúng ta rơi vào else
nhánh, trả về giá trị của `a + sumInts (6, 5).
- Để
sumInts(5, 5)
xác định xem đó sumInts(6, 5)
là gì , chúng tôi cần tạm dừng những gì chúng tôi đang làm và thực hiện cuộc gọi đến sumInts(6, 5)
.
sumInts(6, 5)
được gọi. Nó vào if
chi nhánh và trở về 0
. Tuy nhiên, trường hợp này sumInts
được gọi bởi sumInts(5, 5)
, vì vậy giá trị trả về được truyền lại sumInts(5, 5)
, chứ không phải cho người gọi cấp cao nhất.
sumInts(5, 5)
Bây giờ có thể tính toán 5 + sumInts(6, 5)
để lấy lại 5
. Sau đó, nó trả lại cho người gọi cấp cao nhất.
Lưu ý cách giá trị 5 được hình thành ở đây. Chúng tôi bắt đầu với một cuộc gọi hoạt động đến sumInts
. Điều đó đã loại bỏ một cuộc gọi đệ quy khác và giá trị được trả về bởi cuộc gọi đó đã truyền đạt thông tin trở lại sumInts(5, 5)
. Cuộc gọi đến sumInts(5, 5)
sau đó đã thực hiện một số tính toán và trả lại một giá trị cho người gọi.
Nếu bạn thử điều này với sumInts(4, 5)
, đây là những gì sẽ xảy ra:
sumInts(4, 5)
cố gắng trở về 4 + sumInts(5, 5)
. Để làm điều đó, nó gọi sumInts(5, 5)
.
sumInts(5, 5)
cố gắng trở về 5 + sumInts(6, 5)
. Để làm điều đó, nó gọi sumInts(6, 5)
.
sumInts(6, 5)
trả về 0 trở lại sumInts(5, 5).</li>
<li>
sumInts (5, 5) now has a value for
sumInts (6, 5) , namely 0. It then returns
5 + 0 = 5`.
sumInts(4, 5)
bây giờ có một giá trị cho sumInts(5, 5)
, cụ thể là 5. Sau đó nó trả về 4 + 5 = 9
.
Nói cách khác, giá trị được trả về được hình thành bằng cách tổng hợp từng giá trị một, mỗi lần lấy một giá trị được trả về bởi một lệnh gọi đệ quy cụ thể sumInts
và thêm vào giá trị hiện tại của a
. Khi đệ quy chạm đáy, cuộc gọi sâu nhất trả về 0. Tuy nhiên, giá trị đó không ngay lập tức thoát khỏi chuỗi cuộc gọi đệ quy; thay vào đó, nó chỉ trao lại giá trị cho lệnh gọi đệ quy một lớp phía trên nó. Theo cách đó, mỗi cuộc gọi đệ quy chỉ cần thêm một số nữa và trả lại số đó cao hơn trong chuỗi, kết thúc với tổng kết. Như một bài tập, hãy thử truy tìm nó sumInts(2, 5)
, đó là những gì bạn muốn bắt đầu.
Hi vọng điêu nay co ich!
Đệ quy là một trong những khái niệm lập trình dễ hiểu hơn nhiều về mặt toán học so với mã; có một định nghĩa tốt ở đây
– Lý Huy Trân 11:08:10 05/09/2014
– Lê Thiên Trí 18:47:21 05/09/2014LearnYouARecursion
, hoàn thành bộ vấn đề từ giáo sư đẳng cấp thế giới!Tôi chỉ có để thúc giục bạn gõ "Recursion" trong hộp tìm kiếm của Google. Một trong những quả trứng Phục sinh. Tôi sẽ không làm hỏng bất ngờ cho bạn.
– Trịnh Yến Phương 02:25:44 06/09/2014Bản sao có thể có của stackoverflow.com/questions/25676961/ Kẻ
– Vũ Tiến Hoạt 19:44:09 06/09/2014có thể trùng lặp với đệ quy là gì và khi nào tôi nên sử dụng nó?
– Võ Triều Thành 16:53:38 07/09/2014