Tài nguyên để cải thiện sự hiểu biết của bạn về đệ quy? [đóng cửa]


13

Tôi biết đệ quy là gì (khi một patten lặp lại trong chính nó, điển hình là một hàm tự gọi nó trên một trong các dòng của nó, sau một đột phá có điều kiện ... phải không?), Và tôi có thể hiểu các hàm đệ quy nếu tôi nghiên cứu kỹ chúng. Vấn đề của tôi là, khi tôi nhìn thấy những ví dụ mới, ban đầu tôi luôn bối rối. Nếu tôi thấy một vòng lặp, hoặc ánh xạ, nén, lồng, gọi đa hình, v.v., tôi biết những gì sẽ xảy ra chỉ bằng cách nhìn vào nó. Khi tôi thấy mã đệ quy, quá trình suy nghĩ của tôi thường là 'wtf là đây?' theo sau là 'oh nó đệ quy' theo sau 'Tôi đoán nó phải hoạt động, nếu họ nói nó hoạt động.'

Vì vậy, bạn có bất kỳ lời khuyên / kế hoạch / tài nguyên để xây dựng kỹ năng trong lĩnh vực này? Đệ quy là một khái niệm kỳ lạ, vì vậy tôi nghĩ cách giải quyết nó có thể kỳ lạ và khó hiểu.


28
Để hiểu đệ quy, trước tiên bạn phải hiểu đệ quy.
Andreas Johansson

1
'Con mèo trong chiếc mũ trở lại' của Tiến sĩ Seuss, điều này có thể không hoàn toàn hữu ích, nhưng lời kêu gọi đệ quy về con mèo đã thoát khỏi vết bẩn đáng tiếc đó. :-) Nó cũng có lợi ích là đọc rất nhanh!
DKnight

2
Thực hành thực hành thực hành.
David Thornley

20
Đã trả lời trong câu hỏi này: lập trình
viên.stackexchange.com/

3
@Graham Borland: Đó là một ví dụ về đệ quy vô hạn. Trong hầu hết các chương trình, thiếu trường hợp cơ sở thường dẫn đến lỗi tràn ngăn xếp hoặc lỗi hết bộ nhớ. Đối với người dùng trang web, nó có thể chỉ gây nhầm lẫn. ;)
Thất vọngWithFormsDesigner

Câu trả lời:


10

Bắt đầu với một cái gì đó đơn giản và theo dõi nó bằng bút chì và giấy. Nghiêm túc. Một nơi tốt để bắt đầu là các thuật toán truyền qua cây, vì chúng dễ xử lý hơn bằng cách sử dụng đệ quy so với phép lặp thông thường. Nó không phải là một ví dụ phức tạp, nhưng một cái gì đó đơn giản và bạn có thể làm việc với.

Vâng, thật kỳ lạ và đôi khi phản trực giác, nhưng một khi nó nhấp, một khi bạn nói "Eureka!" bạn sẽ tự hỏi làm thế nào bạn không hiểu nó trước đây! ;) Tôi đã đề xuất cây vì chúng (IMO) là cấu trúc dễ hiểu nhất trong đệ quy và chúng rất dễ làm việc với việc sử dụng bút chì và giấy. ;)


1
+1 đây là cách tôi mò mẫm nó. ví dụ: nếu bạn đang sử dụng OO, hãy tạo một số lớp có mối quan hệ cha mẹ và sau đó thử và tạo một hàm / phương thức để kiểm tra xem một đối tượng có tổ tiên cụ thể không.
Alb

5

Tôi thực sự khuyên bạn nên sử dụng Scheme, sử dụng cuốn sách The Little Lisper. Một khi bạn đã hoàn thành nó, bạn sẽ hiểu đệ quy, sâu bên dưới. Hầu như được đảm bảo.


1
+1 Cuốn sách này thực sự đã làm điều đó cho tôi. Nhưng nó đã được đổi tên thành "The Little Schemer"
mike30

4

Tôi chắc chắn đề nghị SICP. Ngoài ra, bạn nên xem video giới thiệu của các tác giả ở đây ; họ vô cùng cởi mở.

Một con đường khác, không liên quan chặt chẽ đến lập trình, là đọc cuốn sách của Godel, Escher, Bach: a Eternal Golden Braid của Hofstadter. Một khi bạn nhận được máng nó, đệ quy sẽ trông tự nhiên như mỹ phẩm. Ngoài ra, bạn sẽ bị thuyết phục rằng P = nP và bạn sẽ muốn xây dựng những cỗ máy biết suy nghĩ - nhưng đó là một tác dụng phụ quá nhỏ khi so sánh với lợi ích.


GEB dù sao cũng đáng đọc; ngay cả khi một số điều ông nói về một chút ngày tháng (một số tiến bộ về nghiên cứu CS cơ bản đã được thực hiện trong 40 năm qua), sự hiểu biết cơ bản là không.
Donal Fellows

2

Về cơ bản, nó chỉ đi vào thực tiễn ... Hãy xử lý các vấn đề chung (sắp xếp, tìm kiếm, các vấn đề toán học, v.v.) và xem liệu bạn có thể thấy cách giải quyết những vấn đề đó nếu bạn áp dụng một chức năng nhiều lần.

Ví dụ, sắp xếp nhanh hoạt động ở chỗ nó di chuyển phần tử trong danh sách thành hai nửa và sau đó áp dụng lại cho mỗi nửa đó. Khi sắp xếp ban đầu xảy ra, nó không lo lắng về việc hai nửa được sắp xếp tại thời điểm đó. Thay vào đó, nó lấy phần tử trụ và đặt tất cả các phần tử nhỏ hơn phần tử đó ở một bên và tất cả các phần tử lớn hơn hoặc bằng với bên kia. Điều này có ý nghĩa làm thế nào nó có thể gọi đệ quy chính nó tại thời điểm đó để sắp xếp hai danh sách mới nhỏ hơn? Họ cũng liệt kê danh sách. Chỉ cần nhỏ hơn. Nhưng họ vẫn cần được sắp xếp.

Sức mạnh đằng sau đệ quy là sự phân chia và chinh phục khái niệm. Phá vỡ một vấn đề liên tục thành các vấn đề nhỏ hơn giống hệt nhau về bản chất nhưng chỉ nhỏ hơn. Nếu bạn làm điều đó đủ cuối cùng bạn sẽ đến một điểm mà phần còn lại duy nhất đã được giải quyết thì bạn chỉ cần làm việc theo cách của bạn ra khỏi vòng lặp và vấn đề được giải quyết. Nghiên cứu những ví dụ bạn đề cập cho đến khi bạn hiểu chúng. Nó có thể mất một lúc nhưng cuối cùng nó sẽ trở nên dễ dàng hơn. Sau đó cố gắng thực hiện các vấn đề khác và thực hiện một chức năng đệ quy để giải quyết chúng! Chúc may mắn!

EDIT: Tôi cũng phải thêm rằng một yếu tố chính để đệ quy là khả năng được đảm bảo của chức năng để có thể dừng lại. Điều này có nghĩa là sự cố của vấn đề ban đầu phải liên tục trở nên nhỏ hơn và cuối cùng cần phải có một điểm dừng được bảo đảm (một điểm mà vấn đề phụ mới có thể giải quyết được hoặc đã được giải quyết).


Vâng tôi nghĩ rằng tôi đã thấy một lời giải thích sắp xếp nhanh chóng trước đây, tôi có thể hình dung nó hoạt động như thế nào từ lời nhắc của bạn ở trên. Làm thế nào biểu cảm / linh hoạt là đệ quy - hầu hết các vấn đề có thể bị ép buộc vào một cách tiếp cận đệ quy (ngay cả khi nó không tối ưu)? Tôi đã thấy mọi người trả lời các câu đố mã hóa trên mạng rằng hầu hết mọi người đang giải quyết thủ tục, như thể họ có thể sử dụng đệ quy bất cứ khi nào họ muốn chỉ vì địa ngục của nó. Tôi cũng đã đọc một lần, tôi nghĩ rằng một số ngôn ngữ phụ thuộc hoặc đệ quy để thay thế cấu trúc vòng lặp. Và bạn đề cập đến điểm dừng được đảm bảo. Tôi cảm thấy như một trong những điều đó có thể là chìa khóa.
Andrew M

Một vấn đề khởi đầu đơn giản tốt để bạn tự tạo sẽ là viết một chương trình đệ quy tìm giai thừa của một số.
Kenneth

Bất kỳ cấu trúc vòng lặp có thể được đưa vào một cấu trúc đệ quy. Bất kỳ cấu trúc đệ quy nào cũng có thể được đưa vào cấu trúc vòng lặp ... nhiều hay ít. Phải mất thời gian và thực hành để có thể học được khi nào và khi nào không sử dụng đệ quy bởi vì bạn phải nhớ rằng khi bạn sử dụng đệ quy có rất nhiều chi phí sử dụng ở cấp độ phần cứng.
Kenneth

Ví dụ, tôi có thể thấy việc tạo ra một cấu trúc vòng lặp thực hiện sắp xếp nhanh chóng là điều khả thi ... NHƯNG chắc chắn rằng đó là một nỗi đau hoàng gia và tùy thuộc vào cách nó được thực hiện, cuối cùng có thể sử dụng nhiều tài nguyên hệ thống hơn là một hàm đệ quy cho mảng lớn.
Kenneth

Vì vậy, đây là nỗ lực của tôi tại giai thừa. công bằng mà nói tôi đã thấy điều này trước đây và mặc dù tôi đã viết nó từ đầu chứ không phải bộ nhớ, nhưng có lẽ nó vẫn dễ dàng hơn nó sẽ có. Đã thử nó trong JS nhưng có lỗi phân tích cú pháp, nhưng hoạt động trong Python def factorial(number): """return factorial of number""" if number == 0: return 0 elif number == 1: return 1 else: return number * factorial(number - 1)
Andrew M

2

Cá nhân tôi nghĩ rằng đặt cược tốt nhất của bạn là thông qua thực hành.

Tôi đã học đệ quy với LOGO. Bạn có thể sử dụng LISP. Đệ quy là tự nhiên trong các ngôn ngữ. Mặt khác, bạn có thể ví nó như nghiên cứu về các bộ và chuỗi toán học nơi bạn thể hiện những gì tiếp theo dựa trên những gì xuất hiện trước đó, tức là u (n + 1) = f (u (n)) hoặc chuỗi phức tạp hơn trong đó bạn có nhiều biến và nhiều phụ thuộc, ví dụ u (n) = g (u (n-1), u (n-2), v (n), v (n-1)); v (n) = h (u (n-1), u (n-2), v (n), v (n-1)) ...

Vì vậy, đề nghị của tôi là bạn sẽ tìm thấy "các vấn đề" đệ quy tiêu chuẩn (trong biểu hiện của chúng) và thử và thực hiện chúng theo ngôn ngữ bạn chọn. Thực hành sẽ giúp bạn học cách suy nghĩ, đọc và diễn đạt những "vấn đề" đó. Lưu ý rằng thường một số vấn đề này có thể được thể hiện thông qua phép lặp, nhưng đệ quy có thể là một cách thanh lịch hơn để giải quyết chúng. Một trong số đó là cách tính số nhân tử.

"Các vấn đề" đồ họa tôi thấy làm cho nó dễ nhìn hơn. Vì vậy, hãy tìm kiếm các vảy, Fibonacci, đường cong rồng và fractals của Koch nói chung. Nhưng cũng nhìn vào thuật toán sắp xếp nhanh ...

Bạn cần đánh sập một vài chương trình (vòng lặp vô tận, sử dụng tài nguyên vô hạn) và các điều kiện kết thúc không đúng (để có kết quả không mong muốn) trước khi bạn hiểu rõ tất cả. Và ngay cả khi bạn nhận được nó, bạn vẫn sẽ phạm phải những sai lầm đó, chỉ là ít thường xuyên hơn.



0

Nhiều như tôi thích SICPGödel, Escher, Bach: Eternal Golden Braid , Touretzky's LISP: A Gentle Giới thiệu về tính toán tượng trưng cũng làm tốt công việc giới thiệu đệ quy.

Khái niệm cơ bản là thế này: Đầu tiên, bạn phải biết khi nào hàm đệ quy của bạn kết thúc, để nó có thể trả về một kết quả. Sau đó, bạn phải biết cách xử lý vụ việc còn dang dở, và giảm nó thành điều mà bạn có thể tái diễn. Đối với ví dụ giai thừa truyền thống (N), bạn đã kết thúc khi N <= 1 và trường hợp chưa hoàn thành là N * giai thừa (N-1).

Đối với một ví dụ xấu hơn nhiều, có chức năng A (m, n) của Ackermann .

A(0,n) = n+1.                                   This is the terminal case.
A(m,0) = A(m-1,1) if m > 0.                     This is a simple recursion.
A(m,n) = A(m-1, A(m, n-1)) if m > 0 and n > 0.  This one is ugly.

0

Tôi khuyên bạn nên chơi xung quanh với một số ngôn ngữ chức năng kiểu ML như OCaml hoặc Haskell. Tôi thấy rằng cú pháp khớp mẫu thực sự giúp tôi hiểu các hàm đệ quy tương đối phức tạp, chắc chắn tốt hơn nhiều so với các câu lệnh ifvà lược đồ cond. (Tôi đã học Haskell và Scheme cùng một lúc.)

Đây là một ví dụ tầm thường cho sự tương phản:

(define (fib n)
   (cond [(= n 0) 0]
         [(= n 1) 1]
         [else (+ (fib (- n 1)) (fib (- n 2)))]))

và phù hợp với mô hình:

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

Ví dụ này không thực sự làm công lý khác biệt - tôi chưa bao giờ gặp vấn đề với một trong hai phiên bản của chức năng. Nó chỉ để minh họa hai tùy chọn trông như thế nào. Khi bạn nhận được các hàm phức tạp hơn nhiều, sử dụng những thứ như danh sách và cây, sự khác biệt sẽ trở nên rõ rệt hơn nhiều.

Tôi đặc biệt khuyên dùng Haskell vì đây là một ngôn ngữ đơn giản với cú pháp rất hay. Nó cũng làm cho dễ dàng hơn nhiều để làm việc với các ý tưởng nâng cao hơn như corecursion :

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib n = fibs !! n

.


0

Nó không còn xuất bản, nhưng nếu bạn có thể tìm thấy nó, "Thuật toán đệ quy" của Richard Lorentz chẳng liên quan gì đến đệ quy. Nó bao gồm các khái niệm cơ bản về đệ quy, cũng như các thuật toán đệ quy cụ thể.

Các ví dụ có trong Pascal, nhưng không lớn đến mức sự lựa chọn ngôn ngữ gây khó chịu.

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.