it-swarm-vi.com

Trong tiếng Anh đơn giản, đệ quy là gì?

Ý tưởng về đệ quy không phổ biến lắm trong thế giới thực. Vì vậy, nó có vẻ hơi khó hiểu với các lập trình viên mới làm quen. Mặc dù, tôi đoán, họ dần dần quen với khái niệm này. Vì vậy, những gì có thể là một lời giải thích tốt đẹp cho họ để nắm bắt ý tưởng dễ dàng?

76
Gulshan

Để giải thích đệ quy , tôi sử dụng kết hợp nhiều cách giải thích khác nhau, thường là để cả hai cố gắng:

  • giải thích khái niệm
  • giải thích tại sao nó quan trọng
  • giải thích làm thế nào để có được nó.

Để bắt đầu, Wolfram | Alpha định nghĩa nó theo thuật ngữ đơn giản hơn Wikipedia :

Một biểu thức sao cho mỗi thuật ngữ được tạo bằng cách lặp lại một phép toán cụ thể.


Toán học

Nếu học sinh của bạn (hoặc người bạn giải thích cũng vậy, từ bây giờ tôi sẽ nói học sinh) có ít nhất một số nền tảng toán học, rõ ràng là chúng đã gặp phải đệ quy bằng cách nghiên cứu chuỗi và khái niệm của chúng về đệ quy và của họ quan hệ lặp lại .

Một cách rất tốt để bắt đầu sau đó là thể hiện bằng một loạt và nói rằng nó khá đơn giản là những gì đệ quy nói về:

  • một hàm toán học ...
  • ... Nó tự gọi mình để tính toán một giá trị tương ứng với phần tử thứ n ...
  • ... Và xác định một số ranh giới.

Thông thường, bạn có thể nhận được "huh huh, whatev '" vì họ vẫn không sử dụng nó, hoặc nhiều khả năng chỉ là một tiếng ngáy rất sâu.


Ví dụ mã hóa

Đối với phần còn lại, đây thực sự là phiên bản chi tiết của những gì tôi đã trình bày trong Phụ lục của câu trả lời của tôi cho câu hỏi bạn đã chỉ liên quan đến con trỏ (chơi chữ xấu).

Ở giai đoạn này, học sinh của tôi thường biết cách in một cái gì đó lên màn hình. Giả sử chúng ta đang sử dụng C, họ biết cách in một char bằng cách sử dụng write hoặc printf. Họ cũng biết về các vòng điều khiển.

Tôi thường sử dụng một vài vấn đề lập trình đơn giản và lặp đi lặp lại cho đến khi họ hiểu được:

  • giai thừa hoạt động,
  • một máy in bảng chữ cái,
  • một máy in bảng chữ cái đảo ngược,
  • hoạt động lũy thừa .

Nhân tố

Yếu tố là một khái niệm toán học rất đơn giản để hiểu, và việc thực hiện rất gần với biểu diễn toán học của nó. Tuy nhiên, họ có thể không nhận được nó lúc đầu.

Recursive Definition of the Factorial Operation

Bảng chữ cái

Phiên bản bảng chữ cái rất thú vị để dạy họ suy nghĩ về thứ tự của các câu lệnh đệ quy. Giống như với con trỏ, họ sẽ chỉ ném ngẫu nhiên vào bạn. Vấn đề là đưa họ đến nhận ra rằng một vòng lặp có thể được đảo ngược bằng cách sửa đổi các điều kiện [~ # ~] hoặc [~ # ~] bởi chỉ đảo ngược thứ tự của các câu lệnh trong hàm của bạn. Đó là nơi in bảng chữ cái giúp, vì nó là một cái gì đó trực quan cho họ. Đơn giản chỉ cần họ viết một hàm sẽ in một ký tự cho mỗi cuộc gọi và gọi chính nó một cách đệ quy để viết tiếp theo (hoặc trước đó).

Người hâm mộ của FP, bỏ qua thực tế rằng việc in các thứ vào luồng đầu ra là một tác dụng phụ bây giờ ... Chúng ta đừng quá khó chịu trên mặt trận FP. (Nhưng nếu bạn sử dụng một ngôn ngữ có hỗ trợ danh sách, vui lòng ghép nối một danh sách ở mỗi lần lặp và chỉ in kết quả cuối cùng. Nhưng thường thì tôi bắt đầu bằng C, không may là không tốt nhất cho loại vấn đề và khái niệm này) .

lũy thừa

Vấn đề lũy thừa hơi khó khăn hơn một chút ( ở giai đoạn này của việc học). Rõ ràng khái niệm này hoàn toàn giống với một giai thừa và không có sự phức tạp thêm vào ... ngoại trừ việc bạn có nhiều tham số. Và điều đó thường đủ để gây nhầm lẫn cho mọi người và ném chúng ngay từ đầu.

Hình thức đơn giản của nó:

Simple Form of the Exponentiation Operation

có thể được thể hiện như thế này bằng cách tái phát:

Recurrence Relation for the Exponentiation Operation

Cứng hơn

Khi các vấn đề đơn giản này đã được hiển thị VÀ được thực hiện lại trong hướng dẫn, bạn có thể đưa ra các bài tập khó hơn (nhưng rất cổ điển):

  • Fibonacci số,
  • Số chia chung lớn nhất ,
  • Vấn đề 8-Queens ,
  • Trò chơi Tháp Hà Nội ,
  • Và nếu bạn có một môi trường đồ họa (hoặc có thể cung cấp cuống mã cho nó hoặc cho đầu ra thiết bị đầu cuối hoặc họ có thể quản lý điều đó rồi), những thứ như: [.__.]
  • Và đối với các ví dụ thực tế, hãy xem xét việc viết: [.__.]
    • một thuật toán truyền tải cây,
    • một trình phân tích cú pháp biểu thức toán học đơn giản,
    • một trò chơi quét mìn.

Lưu ý: Một lần nữa, một số trong số này thực sự không khó hơn ... Họ chỉ tiếp cận vấn đề từ cùng một góc độ, hoặc một góc hơi khác. Nhưng thực hành làm cho hoàn hảo.


Người giúp việc

Tham chiếu

Một số đọc không bao giờ đau. Chà, lúc đầu nó sẽ như vậy, và họ sẽ còn cảm thấy lạc lõng hơn nữa. Đó là thứ phát triển trong bạn và nằm ở phía sau đầu bạn cho đến một ngày bạn nhận ra rằng cuối cùng bạn đã nhận được nó. Và sau đó bạn nghĩ lại những thứ bạn đọc. đệ quy , đệ quy trong Khoa học máy tínhquan hệ lặp lại các trang trên Wikipedia sẽ làm ngay bây giờ.

Cấp/Độ sâu

Giả sử sinh viên của bạn không có nhiều kinh nghiệm mã hóa, hãy cung cấp cuống mã. Sau những lần thử đầu tiên, hãy cung cấp cho họ chức năng in có thể hiển thị mức đệ quy. In giá trị số của cấp độ giúp.

Sơ đồ ngăn xếp ngăn xếp

Việc thụt vào một kết quả được in (hoặc đầu ra của cấp độ) cũng giúp ích, vì nó cung cấp một biểu diễn trực quan khác về những gì chương trình của bạn đang làm, mở và đóng các bối cảnh ngăn xếp như ngăn kéo hoặc thư mục trong Explorer hệ thống tệp.

Từ viết tắt đệ quy

Nếu sinh viên của bạn đã thành thạo một chút về văn hóa máy tính, họ có thể đã sử dụng một số dự án/phần mềm có tên bằng cách sử dụng từ viết tắt đệ quy . Đó là một truyền thống xuất hiện trong một thời gian, đặc biệt là trong các dự án GNU. Một số ví dụ bao gồm:

Đệ quy:

  • GNU - "GNU không phải Unix"
  • Nagios - "Nagios Ain't Gonna khăng khăng về vị thánh"
  • PHP - "Bộ xử lý siêu văn bản PHP" (và nguồn gốc là "Trang chủ cá nhân")
  • Rượu vang - "Rượu không phải là trình giả lập"
  • Zile - "Zile là mất mát Emacs"

Đệ quy lẫn nhau:

  • HURD - "HIRD của Daemon thay thế Unix" (trong đó HIRD là "HURD của các giao diện đại diện cho độ sâu")

Có họ cố gắng để đưa ra với riêng của họ.

Tương tự, có nhiều sự xuất hiện của sự hài hước đệ quy, như tìm kiếm đệ quy của Google chỉnh sửa. Để biết thêm thông tin về đệ quy, hãy đọc câu trả lời này .


Cạm bẫy và học hỏi thêm

Một số vấn đề mà mọi người thường đấu tranh và bạn cần biết câu trả lời.

Tại sao, trời ơi Tại sao ???

Tại sao bạn lại làm vậy? Một lý do tốt nhưng không rõ ràng là thường đơn giản hơn để diễn đạt một vấn đề theo cách đó. Một lý do không tốt nhưng rõ ràng là nó thường mất ít thao tác gõ hơn (đừng khiến họ cảm thấy loot l33t vì chỉ sử dụng đệ quy mặc dù ...).

Một số vấn đề chắc chắn dễ giải quyết hơn khi sử dụng phương pháp đệ quy. Thông thường, bất kỳ vấn đề nào bạn có thể giải quyết bằng cách sử dụng Phân chia và chinh phục mô hình sẽ phù hợp với thuật toán đệ quy đa nhánh.

N là gì nữa ??

Tại sao n hoặc (bất cứ tên biến của bạn) khác nhau mỗi lần? Người mới bắt đầu thường gặp vấn đề trong việc hiểu biến và tham số là gì và làm thế nào để những thứ có tên n trong chương trình của bạn có thể có các giá trị khác nhau. Vì vậy, bây giờ nếu giá trị này nằm trong vòng điều khiển hoặc đệ quy, điều đó thậm chí còn tồi tệ hơn! Hãy tử tế và không sử dụng cùng một tên biến ở mọi nơi và làm rõ rằng các tham số chỉ là các biến .

Điều kiện kết thúc

Làm thế nào để tôi xác định tình trạng cuối cùng của tôi? Điều đó thật dễ dàng, chỉ cần họ nói to các bước. Chẳng hạn, giai thừa bắt đầu từ 5, rồi 4, rồi ... cho đến 0.

Quỷ dữ nằm trong Chi tiết

Đừng nói chuyện với những thứ sớm như tối ưu hóa cuộc gọi đuôi . Tôi biết, tôi biết, TCO rất hay, nhưng lúc đầu họ không quan tâm. Cung cấp cho họ một chút thời gian để quấn đầu xung quanh quá trình theo cách phù hợp với họ. Hãy thoải mái phá vỡ thế giới của họ một lần nữa sau đó, nhưng hãy cho họ nghỉ ngơi.

Tương tự, đừng nói thẳng từ bài giảng đầu tiên về ngăn xếp cuộc gọi và mức tiêu thụ bộ nhớ của nó và ... à ... tràn ngăn xếp . Tôi thường dạy kèm cho sinh viên một cách riêng tư, người chỉ cho tôi những bài giảng về họ có 50 slide về mọi thứ Có để biết về đệ quy khi họ hầu như không thể viết một vòng lặp chính xác ở giai đoạn này. Đó là một ví dụ tốt về cách một tài liệu tham khảo sẽ giúp sau nhưng ngay bây giờ chỉ cần nhầm lẫn bạn sâu sắc.

Nhưng xin vui lòng, trong thời gian thích hợp, hãy làm rõ rằng có lý do để đi theo lộ trình lặp hoặc đệ quy .

Đệ quy lẫn nhau

Chúng ta đã thấy rằng các hàm có thể được đệ quy và thậm chí chúng có thể có nhiều điểm gọi (8-Queen, Hà Nội, Fibonacci hoặc thậm chí là một thuật toán thăm dò cho một người quét mìn). Nhưng còn các cuộc gọi đệ quy lẫn nha thì sao? Bắt đầu với toán học ở đây là tốt. f(x) = g(x) + h(x) trong đó g(x) = f(x) + l(x)hl chỉ làm công cụ.

Bắt đầu chỉ với loạt toán học giúp viết và thực hiện dễ dàng hơn vì hợp đồng được xác định rõ ràng bằng các biểu thức. Chẳng hạn, Trình tự nam và nữ của Hofstadter :

Hofstadter's Male and Female Sequences

Tuy nhiên, về mặt mã, cần lưu ý rằng việc triển khai giải pháp đệ quy lẫn nhau thường dẫn đến sao chép mã và nên được sắp xếp hợp lý thành một dạng đệ quy duy nhất (Xem Peter Norvig ' Giải từng câu đố Sudok .

111
haylem

Việc gọi một hàm từ bên trong cùng hàm đó.

59
ChaosPandion

Đệ quy là một hàm gọi chính nó.

Làm thế nào để sử dụng nó, khi nào sử dụng nó và làm thế nào để tránh thiết kế xấu là điều quan trọng cần biết, điều này đòi hỏi bạn phải tự mình thử nó và hiểu những gì xảy ra.

Điều quan trọng nhất bạn cần biết là phải rất cẩn thận để không có một vòng lặp không bao giờ kết thúc. Câu trả lời từ pramodc84 cho câu hỏi của bạn có lỗi này: Nó không bao giờ kết thúc ...
[.___.] Một hàm đệ quy phải luôn kiểm tra một điều kiện để xác định xem nó có nên tự gọi lại hay không.

Ví dụ kinh điển nhất để sử dụng đệ quy, là làm việc với một cây không có giới hạn tĩnh về chiều sâu. Đây là một nhiệm vụ mà bạn phải sử dụng đệ quy.

27
awe

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ó.

Mọi hàm đệ quy có xu hướng:

  1. lấy một danh sách để xử lý, hoặc một số cấu trúc khác, hoặc miền vấn đề
  2. đối phó với điểm/bước hiện tại
  3. gọi chính nó trên (các) phần còn lại/tên miền phụ
  4. kết hợp hoặc sử dụng kết quả của công việc tên miền phụ

Khi bước 2 ở trước 3 và khi bước 4 là tầm thường (phép nối, tổng hoặc không có gì) thì điều này cho phép đệ quy đuôi . Bước 2 thường phải đến sau bước 3, vì các kết quả từ (các) tên miền phụ của vấn đề có thể cần thiết để hoàn thành bước hiện tại.

Đi qua một cây nhị phân thẳng về phía trước. Việc truyền tải có thể được thực hiện theo thứ tự trước, theo thứ tự hoặc sau khi đặt hàng, tùy thuộc vào những gì được yêu cầu.

   B
A     C

Đặt hàng trước: B A C

traverse(tree):
    visit the node
    traverse(left)
    traverse(right)

Theo thứ tự: A B C

traverse(tree):
    traverse(left)
    visit the node
    traverse(right)

Đặt hàng sau: A C B

traverse(tree):
    traverse(left)
    traverse(right)
    visit the node

Rất nhiều vấn đề đệ quy là các trường hợp cụ thể của một bản đồ hoạt động hoặc một gấp - chỉ hiểu hai thao tác này có thể dẫn đến sự hiểu biết đáng kể về các trường hợp sử dụng tốt cho đệ quy.

21
Orbling

OP nói rằng đệ quy không tồn tại trong thế giới thực, nhưng tôi xin khác.

Chúng ta hãy thực hiện "hoạt động" trong thế giới thực của việc cắt một chiếc bánh pizza. Bạn đã lấy pizza ra khỏi lò và để phục vụ nó, bạn phải cắt nó làm đôi, sau đó cắt một nửa, sau đó lại cắt đôi một nửa.

Hoạt động cắt bánh pizza bạn thực hiện lặp đi lặp lại cho đến khi bạn có kết quả bạn muốn (số lát). Và để tranh luận, hãy nói rằng một chiếc bánh pizza không cắt là một lát.

Đây là một ví dụ trong Ruby:

[.___] def cut_pizza (current_slices, wish_slices) [.__.] nếu current_slices! = wish_slices [.__.] # chúng tôi chưa có đủ lát cắt để cung cấp cho mọi người, vì vậy [.__.] cắt các lát bánh pizza, do đó nhân đôi số lượng của chúng [.__.] new_slices = current_slices * 2 [.__.] # và đây là cuộc gọi đệ quy [.__.] cut_pizza (new_slices, wish_slices) [.__.] khác [. .___] .] [.__.] pizza = 1 # toàn bộ pizza, 'một lát' [.__.] cut_pizza (pizza, 8) # => chúng tôi sẽ nhận được 8 [.__.]

Vì vậy, hoạt động trong thế giới thực đang cắt một chiếc bánh pizza, và đệ quy đang làm điều tương tự lặp đi lặp lại cho đến khi bạn có những gì bạn muốn.

Các hoạt động bạn sẽ thấy rằng cắt xén mà bạn có thể thực hiện với các hàm đệ quy là:

  • Tính lãi kép trong một số tháng.
  • Tìm kiếm một tệp trên một hệ thống tệp (vì hệ thống tệp là cây vì các thư mục).
  • Bất cứ điều gì liên quan đến làm việc với cây nói chung, tôi đoán.

Tôi khuyên bạn nên viết chương trình để tìm tệp dựa trên tên tệp của nó và cố gắng viết một hàm tự gọi cho đến khi tìm thấy, chữ ký sẽ trông như thế này:

find_file_by_name(file_name_we_are_looking_for, path_to_look_in)

Vì vậy, bạn có thể gọi nó như thế này:

find_file_by_name('httpd.conf', '/etc') # damn it i can never find Apache's conf

Theo tôi, đó chỉ đơn giản là cơ chế lập trình, một cách khéo léo loại bỏ sự trùng lặp. Bạn có thể viết lại điều này bằng cách sử dụng các biến, nhưng đây là một giải pháp 'đẹp hơn'. Không có gì bí ẩn hoặc khó khăn về nó. Bạn sẽ viết một vài hàm đệ quy, nó sẽ nhấp và huzzah một thủ thuật cơ học khác trong hộp công cụ lập trình của bạn.

Tín dụng thêmcut_pizza ví dụ ở trên sẽ cung cấp cho bạn một lỗi cấp độ ngăn xếp quá sâu nếu bạn yêu cầu nó cho một số lát không có sức mạnh bằng 2 (tức là 2 hoặc 4 hoặc 8 hoặc 16). Bạn có thể sửa đổi nó để nếu ai đó yêu cầu 10 lát nó sẽ không chạy mãi mãi không?

20
Ryan Allen

Được rồi tôi sẽ cố gắng để giữ cho đơn giản và ngắn gọn này.

Hàm đệ quy là các hàm tự gọi. Hàm đệ quy bao gồm ba điều:

  1. Hợp lý
  2. Một cuộc gọi đến chính nó
  3. Khi nào chấm dứt.

Cách tốt nhất để viết các phương thức đệ quy, là nghĩ về phương thức mà bạn đang cố gắng viết như một ví dụ đơn giản chỉ xử lý một vòng lặp của quy trình bạn muốn lặp lại, sau đó thêm lệnh gọi vào chính phương thức đó và thêm khi bạn muốn chấm dứt. Cách tốt nhất để học là thực hành như tất cả mọi thứ.

Vì đây là trang web lập trình viên nên tôi sẽ không viết mã nhưng đây là một link

nếu bạn có trò đùa đó, bạn hiểu ý nghĩa của đệ quy.

16
dustyprogrammer

Recursion là một công cụ mà một lập trình viên có thể sử dụng để gọi một hàm gọi chính nó. Chuỗi Fibonacci là ví dụ trong sách giáo khoa về cách sử dụng đệ quy.

Hầu hết các mã đệ quy nếu không phải tất cả đều có thể được biểu diễn dưới dạng hàm lặp, nhưng nó thường lộn xộn. Ví dụ điển hình của các chương trình đệ quy khác là Cấu trúc dữ liệu như cây, cây tìm kiếm nhị phân và thậm chí quicksort.

Đệ quy được sử dụng để làm cho mã ít cẩu thả hơn, hãy nhớ rằng nó thường chậm hơn và đòi hỏi nhiều bộ nhớ hơn.

6
Bryan Harrington

Tôi thích sử dụng cái này:

Làm thế nào để bạn đi bộ đến cửa hàng?

Nếu bạn đang ở lối vào cửa hàng, chỉ cần đi qua nó. Nếu không, hãy thực hiện một bước, sau đó đi bộ phần còn lại của cửa hàng đến cửa hàng.

Điều quan trọng là bao gồm ba khía cạnh:

  • Một trường hợp cơ sở tầm thường
  • Giải quyết một phần nhỏ của vấn đề
  • Giải quyết phần còn lại của bài toán một cách đệ quy

Chúng tôi thực sự sử dụng đệ quy rất nhiều trong cuộc sống hàng ngày; chúng ta không nghĩ về nó theo cách đó.

5
Barry Brown

Ví dụ tốt nhất mà tôi muốn chỉ cho bạn là Ngôn ngữ lập trình C của K & R. Trong cuốn sách đó (và tôi đang trích dẫn từ bộ nhớ), mục trong trang chỉ mục để đệ quy (một mình) liệt kê trang thực tế nơi họ nói về đệ quy và trang chỉ mục là tốt.

3
Kanini

Josh K đã đề cập đến Matroshka búp bê. Giả sử rằng bạn muốn học một cái gì đó mà chỉ con búp bê ngắn nhất mới biết. Vấn đề là bạn không thể thực sự nói chuyện trực tiếp với cô ấy, vì ban đầu cô ấy sống bên trong con búp bê cao hơn trên bức ảnh đầu tiên được đặt bên trái cô ấy. Cấu trúc này diễn ra như vậy (một con búp bê sống bên trong con búp bê cao hơn) cho đến khi chỉ kết thúc với con cao nhất.

Vì vậy, điều duy nhất bạn có thể làm là đặt câu hỏi của bạn cho con búp bê cao nhất. Con búp bê cao nhất (người không biết câu trả lời) sẽ cần chuyển câu hỏi của bạn cho con búp bê ngắn hơn (mà trên bức ảnh đầu tiên nằm bên phải cô ấy). Vì cô ấy cũng không có câu trả lời, cô ấy cần hỏi con búp bê ngắn hơn tiếp theo. Điều này sẽ diễn ra như vậy cho đến khi tin nhắn đến được con búp bê ngắn nhất. Con búp bê ngắn nhất (là người duy nhất biết câu trả lời bí mật) sẽ chuyển câu trả lời cho con búp bê cao hơn tiếp theo (được tìm thấy ở bên trái của cô), nó sẽ chuyển nó cho con búp bê cao hơn tiếp theo ... và điều này sẽ tiếp tục cho đến khi câu trả lời đạt đến đích cuối cùng, đó là con búp bê cao nhất và cuối cùng là ... bạn :)

Đây là những gì đệ quy thực sự làm. Một hàm/phương thức gọi chính nó cho đến khi nhận được câu trả lời mong đợi. Đó là lý do tại sao khi bạn viết mã đệ quy, điều rất quan trọng là quyết định khi nào đệ quy nên chấm dứt.

Không phải là lời giải thích tốt nhất nhưng nó hy vọng sẽ giúp.

2
sakisk

Recursion n. - Một mẫu thiết kế thuật toán trong đó một hoạt động được xác định theo chính nó.

Ví dụ kinh điển là tìm giai thừa của một số, n!. 0! = 1 và với bất kỳ số tự nhiên N nào khác, giai thừa của N là tích của tất cả các số tự nhiên nhỏ hơn hoặc bằng N. Vì vậy, 6! = 6 * 5 * 4 * 3 * 2 * 1 = 720. Định nghĩa cơ bản này sẽ cho phép bạn tạo một giải pháp lặp đơn giản:

int Fact(int degree)
{
    int result = 1;
    for(int i=degree; i>1; i--)
       result *= i;

    return result;
}

Tuy nhiên, kiểm tra hoạt động một lần nữa. 6! = 6 * 5 * 4 * 3 * 2 * 1. Theo định nghĩa tương tự, 5! = 5 * 4 * 3 * 2 * 1, nghĩa là chúng ta có thể nói 6! = 6 * (5!). Lần lượt, 5! = 5 * (4!) Và cứ thế. Bằng cách này, chúng tôi giảm vấn đề thành một hoạt động được thực hiện trên kết quả của tất cả các hoạt động trước đó. Điều này cuối cùng giảm đến một điểm, được gọi là trường hợp cơ sở, trong đó kết quả được biết theo định nghĩa. Trong trường hợp của chúng tôi, 0! = 1 (trong hầu hết các trường hợp, chúng tôi cũng có thể nói rằng 1! = 1). Trong điện toán, chúng ta thường được phép định nghĩa các thuật toán theo cách rất giống nhau, bằng cách tự gọi phương thức và chuyển một đầu vào nhỏ hơn, do đó giảm vấn đề thông qua nhiều lần truy cập vào trường hợp cơ sở:

int Fact(int degree)
{
    if(degree==0) return 1; //the base case; 0! = 1 by definition
    else return degree * Fact(degree -1); //the recursive case; N! = N*(N-1)!
}

Điều này có thể, trong nhiều ngôn ngữ, có thể được đơn giản hóa hơn nữa bằng cách sử dụng toán tử ternary (đôi khi được xem là hàm Iif trong các ngôn ngữ không cung cấp toán tử như vậy):

int Fact(int degree)
{
    //reads equivalently to the above, but is concise and often optimizable
    return degree==0 ? 1: degree * Fact(degree -1);
}

Ưu điểm:

  • Biểu thức tự nhiên - đối với nhiều loại thuật toán, đây là một cách rất tự nhiên để diễn đạt hàm.
  • Giảm LỘC - Nó thường ngắn gọn hơn nhiều để định nghĩa một hàm đệ quy.
  • Tốc độ - Trong một số trường hợp nhất định, tùy thuộc vào ngôn ngữ và kiến ​​trúc máy tính, đệ quy thuật toán nhanh hơn giải pháp lặp tương đương, thường là do thực hiện cuộc gọi hàm là thao tác nhanh hơn ở cấp phần cứng so với các thao tác và truy cập bộ nhớ cần thiết để lặp lại.
  • Tính phân chia - Nhiều thuật toán đệ quy có tâm lý "phân chia và chinh phục"; kết quả của hoạt động là một chức năng của kết quả của cùng một hoạt động được thực hiện trên mỗi hai nửa của đầu vào. Điều này cho phép bạn chia công việc thành hai ở mỗi cấp độ và nếu có sẵn, bạn có thể giao cho nửa kia cho một "đơn vị thực hiện" khác để xử lý. Điều này thường khó hơn hoặc không thể với thuật toán lặp.

Nhược điểm:

  • Yêu cầu hiểu - Đơn giản là bạn phải "nắm bắt" khái niệm đệ quy để hiểu những gì đang diễn ra, và do đó viết và duy trì các thuật toán đệ quy hiệu quả. Nếu không, nó trông giống như ma thuật đen.
  • Phụ thuộc vào bối cảnh - Việc đệ quy có phải là một ý tưởng tốt hay không phụ thuộc vào mức độ trang nhã của thuật toán có thể được định nghĩa theo chính nó. Mặc dù có thể xây dựng, ví dụ, SelectionSort đệ quy, thuật toán lặp thường là dễ hiểu hơn.
  • Trades RAM quyền truy cập cho ngăn xếp cuộc gọi - Thông thường, các lệnh gọi hàm rẻ hơn truy cập bộ đệm, có thể thực hiện đệ quy nhanh hơn lặp lại. Nhưng, thường có giới hạn về độ sâu của ngăn xếp cuộc gọi có thể gây ra đệ quy cho lỗi trong đó một thuật toán lặp sẽ hoạt động.
  • Đệ quy vô hạn - Bạn phải biết khi nào nên dừng lại. Lặp lại vô hạn cũng có thể nhưng các cấu trúc lặp có liên quan thường dễ hiểu hơn và do đó để gỡ lỗi.
2
KeithS

Ví dụ tôi sử dụng là một vấn đề tôi gặp phải trong cuộc sống thực. Bạn có một hộp đựng (chẳng hạn như một chiếc ba lô lớn mà bạn dự định thực hiện trong một chuyến đi) và bạn muốn biết tổng trọng lượng. Bạn có trong hai hoặc ba vật chứa lỏng lẻo và một số vật chứa khác (ví dụ, bao tải.) Trọng lượng của tổng container rõ ràng là trọng lượng của container rỗng cộng với trọng lượng của mọi thứ trong đó. Đối với các mặt hàng lỏng lẻo, bạn chỉ có thể cân chúng, và đối với các bao tải bạn chỉ có thể cân chúng hoặc bạn có thể nói "trọng lượng của mỗi bao là trọng lượng của thùng rỗng cộng với trọng lượng của mọi thứ trong đó". Và sau đó bạn tiếp tục đi vào các container vào các thùng chứa và cứ thế cho đến khi bạn đến một điểm mà chỉ có các vật phẩm lỏng lẻo trong một container. Đó là đệ quy.

Bạn có thể nghĩ rằng điều đó không bao giờ xảy ra trong cuộc sống thực, nhưng hãy tưởng tượng bạn đang cố gắng đếm, hoặc tăng lương cho những người trong một công ty hoặc bộ phận cụ thể, có sự pha trộn của những người chỉ làm việc cho công ty, những người trong bộ phận, sau đó các bộ phận có các phòng ban và như vậy. Hoặc bán hàng ở một quốc gia có các khu vực, một số trong đó có các tiểu vùng, v.v ... Những loại vấn đề này xảy ra mọi lúc trong kinh doanh.

1
Kate Gregory

Đệ quy có thể được sử dụng để giải quyết rất nhiều vấn đề đếm. Ví dụ: giả sử bạn có một nhóm n người trong một bữa tiệc (n> 1) và mọi người đều bắt tay người khác chính xác một lần. Có bao nhiêu cái bắt tay diễn ra? Bạn có thể biết rằng giải pháp là C (n, 2) = n (n-1)/2, nhưng bạn có thể giải đệ quy như sau:

Giả sử chỉ có hai người. Sau đó (bằng cách kiểm tra) câu trả lời rõ ràng là 1.

Giả sử bạn có ba người. Độc thân một người, và lưu ý rằng anh ấy/cô ấy bắt tay với hai người khác. Sau đó, bạn phải đếm chỉ những cái bắt tay giữa hai người kia. Chúng tôi đã làm điều đó ngay bây giờ, và nó là 1. Vì vậy, câu trả lời là 2 + 1 = 3.

Giả sử bạn có n người. Theo logic tương tự như trước, đó là (n-1) + (số lần bắt tay giữa n-1 người). Mở rộng, chúng tôi nhận được (n-1) + (n-2) + ... + 1.

Được biểu thị như một hàm đệ quy,

f (2) = 1
[.__.] f (n) = n-1 + f (n-1), n> 2

0
Mitch Schwartz

Đây là một ví dụ thực tế cho đệ quy.

Hãy để họ tưởng tượng rằng họ có một bộ sưu tập truyện tranh và bạn sẽ trộn tất cả lại thành một đống lớn. Cẩn thận - nếu họ thực sự có một bộ sưu tập, họ có thể giết bạn ngay lập tức khi bạn chỉ đề cập đến ý tưởng để làm như vậy.

Bây giờ hãy để họ sắp xếp đống truyện tranh lớn chưa được sắp xếp này với sự giúp đỡ của hướng dẫn này:

Manual: How to sort a pile of comics

Check the pile if it is already sorted. If it is, then done.

As long as there are comics in the pile, put each one on another pile, 
ordered from left to right in ascending order:

    If your current pile contains different comics, pile them by comic.
    If not and your current pile contains different years, pile them by year.
    If not and your current pile contains different tenth digits, pile them 
    by this digit: Issue 1 to 9, 10 to 19, and so on.
    If not then "pile" them by issue number.

Refer to the "Manual: How to sort a pile of comics" to separately sort each
of the new piles.

Collect the piles back to a big pile from left to right.

Done.

Điều thú vị ở đây là: Khi họ gặp vấn đề đơn lẻ, họ có đầy đủ "khung ngăn xếp" với các cọc cục bộ có thể nhìn thấy trước mặt họ. Cung cấp cho họ nhiều bản in của sổ tay và đặt một cấp cho mỗi cấp độ cọc với một dấu mà bạn hiện đang ở cấp độ này (nghĩa là trạng thái của các biến cục bộ), để bạn có thể tiếp tục ở đó trên mỗi Xong.

Đó là những gì đệ quy về cơ bản là: Thực hiện cùng một quy trình, chỉ ở mức độ chi tiết tốt hơn, bạn càng đi sâu vào nó.

0
Secure

Trong cuộc sống (trái ngược với trong một chương trình máy tính), sự đệ quy hiếm khi xảy ra dưới sự kiểm soát trực tiếp của chúng tôi, bởi vì nó có thể gây nhầm lẫn khi thực hiện. Ngoài ra, nhận thức có xu hướng về các tác dụng phụ, thay vì thuần túy về mặt chức năng, vì vậy nếu sự đệ quy đang xảy ra, bạn có thể không nhận thấy điều đó.

Đệ quy không xảy ra ở đây trên thế giới. Rất nhiều.

Một ví dụ điển hình là (một phiên bản đơn giản hóa) của chu trình nước:

  • Mặt trời làm nóng hồ
  • Nước lên trời và tạo thành mây
  • Những đám mây trôi qua một ngọn núi
  • Ở trên núi không khí trở nên quá lạnh khiến độ ẩm của chúng bị giữ lại
  • Mưa rơi
  • Một dòng sông hình thành
  • Nước trong sông chảy vào hồ

Đây là một chu kỳ khiến cho bản thân của nó xảy ra một lần nữa. Đó là đệ quy.

Một nơi khác bạn có thể nhận được đệ quy là bằng tiếng Anh (và ngôn ngữ của con người nói chung). Ban đầu bạn có thể không nhận ra nó, nhưng cách chúng ta có thể tạo một câu là đệ quy, bởi vì các quy tắc cho phép chúng ta nhúng một thể hiện của một biểu tượng vào một thể hiện khác của cùng một biểu tượng.

Từ Bản năng ngôn ngữ của Steven Pinker's:

nếu cô gái ăn kem hoặc cô gái ăn kẹo thì chàng trai ăn xúc xích

Đó là toàn bộ câu có chứa toàn bộ câu khác:

cô gái ăn kem

cô gái ăn kẹo

cậu bé ăn xúc xích

Hành động hiểu toàn bộ câu liên quan đến việc hiểu các câu nhỏ hơn, trong đó sử dụng cùng một bộ mánh khóe tinh thần để được hiểu là toàn bộ câu.

Để hiểu đệ quy từ góc độ lập trình, dễ nhất là xem xét một vấn đề có thể giải quyết bằng đệ quy và hiểu tại sao nó nên và điều đó có nghĩa là bạn cần phải làm gì.

Ví dụ, tôi sẽ sử dụng hàm ước số chung lớn nhất hoặc viết tắt là gcd.

Bạn có hai số ab. Để tìm gcd của chúng (giả sử không phải là 0), bạn cần kiểm tra xem a có chia hết cho b không. Nếu đó là b là gcd, nếu không, bạn cần kiểm tra gcd của b và phần còn lại của a/b.

Bạn đã có thể thấy rằng đây là một hàm đệ quy, vì bạn có hàm gcd gọi hàm gcd. Chỉ cần đưa nó về nhà, đây là c # (một lần nữa, giả sử 0 không bao giờ được truyền vào dưới dạng tham số):

int gcd(int a, int b)
{   
    if (a % b == 0) //this is a stopping condition
    {
        return b;
    }

    return (gcd(b, a % b)); //the call to gcd here makes this function recursive
}

Trong một chương trình, điều quan trọng là phải có một điều kiện dừng, nếu không chức năng của bạn sẽ tái diễn mãi mãi, điều này cuối cùng sẽ gây ra tràn ngăn xếp!

Lý do để sử dụng đệ quy ở đây, thay vì vòng lặp while hoặc một số cấu trúc lặp khác, là khi bạn đọc mã, nó sẽ cho bạn biết nó đang làm gì và điều gì sẽ xảy ra tiếp theo, vì vậy sẽ dễ dàng hơn nếu nó hoạt động chính xác .

0
Matt Ellen