đệ quy nghĩa là gì? Sự khác biệt giữa đệ quy trong các ngôn ngữ lập trình khác nhau. Cách giải các bài toán đệ quy

Từ tiếng Latin đệ quy (trở lại). TRONG trường hợp chungĐây là tên được đặt cho quá trình lặp lại các phần tử theo cách “tự tương tự”.

Một ví dụ nổi bật về đệ quy là búp bê lồng nhau. Định nghĩa đệ quy: “matryoshka là một con búp bê bằng gỗ rỗng có thể tháo rời, chứa một con búp bê làm tổ bên trong. kích thước nhỏ hơn" Đây là sự đệ quy trong tiếng Nga. Và nếu không có giới hạn về khả năng của các bậc thầy thì búp bê matryoshka lý tưởng sẽ đi sâu vào bản thân đến cấp độ nguyên tử. Hoặc thậm chí sâu hơn. Đơn giản là Lefty không có ống ngắm nhỏ đủ mạnh. Giới hạn trên về mặt lý thuyết cũng không giới hạn, nhưng bao báp kích thước phù hợp không phát triển trên hành tinh của chúng ta. Nhìn chung, theo lý do kỹ thuậtđệ quy phải hữu hạn.

Trong lập trình (như trong toán học), đệ quy là quá trình một hàm gọi chính nó (đệ quy trực tiếp) hoặc gọi hàm B từ bên trong hàm A, hàm này chứa lệnh gọi hàm A (đệ quy gián tiếp hoặc lẫn nhau). Tất nhiên, lệnh gọi đệ quy phải có điều kiện thoát thỏa mãn, nếu không chương trình sẽ bị treo, như trong vòng lặp vô hạn - nhưng, không giống như vòng lặp vô hạn, phép đệ quy vô hạn sẽ gặp sự cố khi tràn ngăn xếp.

Ví dụ đệ quy

Ví dụ khó chịu nhất về đệ quy trong lập trình toán học là tính giai thừa. Chúng ta đừng thay đổi những truyền thống vẻ vang của mình. Dành cho ai chưa lấy: N! (giai thừa N) là tích của tất cả số tự nhiên từ một đến N (giai thừa của 0 là 1).
Bạn có thể nhân các số từ 1 đến N trong một vòng lặp một cách ngu ngốc. Hoặc bạn có thể xây dựng một hàm giai thừa(n), hàm này sẽ chứa một điều kiện và lệnh gọi chính hàm đó. Nếu n bằng 1 thì hàm trả về giá trị 1, nếu không thì trả về giá trị của n nhân với giai thừa(n-1).
Phác thảo trong PHP

Hàm giai thừa($n) ( if ($n == 1) ( return 1; ) else ( return intval($n * giai thừa($n - 1)); ) )

Ứng dụng thực tế của đệ quy

“Chà, tại sao điều này lại cần thiết ở đây?” - một độc giả trẻ thiếu kiên nhẫn sẽ hỏi chúng tôi - “Sự vô nghĩa về mặt khoa học, sự tẻ nhạt, đủ thứ giai thừa… Nhưng thực tế, tại sao lại áp dụng đệ quy này?”
“Về con mắt đen của lập trình web,” chúng tôi sẽ trả lời không do dự. Và chúng tôi sẽ biện minh cho điều này ngay lập tức.

Trên thực tế, có nhiều cách sử dụng đệ quy trong lập trình web hơn bạn tưởng. Bởi vì đệ quy có lẽ là cách duy nhất để duyệt qua bất kỳ cấu trúc cây nào khi cả kích thước lẫn độ sâu lồng của nó đều không được biết trước. Nhân tiện, việc xây dựng và duyệt đồ thị cũng không thể thực hiện được nếu không có nó. Đây là một cách cổ điển, thưa quý vị - hãy thử tìm kiếm theo cách khác tập tin cần thiết trong cây thư mục Unix và bạn sẽ ngay lập tức hiểu rõ rằng không có đệ quy thì sẽ không có nơi nào cả.

Cố gắng làm mà không cần nó bằng cách xây dựng sơ đồ trang web có cấu trúc phân cấp của các phần dưới dạng danh sách lồng nhau. Bạn thà treo cổ tự tử còn hơn xây dựng nó nếu bạn không biết trước chính xác độ sâu làm tổ được giới hạn ở bao nhiêu cấp. Và ngay cả khi bạn biết, nhưng cố gắng làm mà không đệ quy, thì thay vì một chức năng đơn giản, minh bạch và không an toàn, bạn sẽ xây dựng một phần mềm cồng kềnh “kệ nạng”. Và khi bạn lau xong mồ hôi trên trán, sự thật nghiệt ngã của cuộc sống sẽ hiện ra trong bạn: nếu bạn thay đổi độ sâu làm tổ, cấu trúc trải rộng của bạn sẽ ngay lập tức ngừng hoạt động bình thường. Vì vậy, bạn khó có thể sử dụng nó ở bất cứ nơi nào khác.

Đệ quy trong công cụ tìm kiếm

Đúng chính xác. Công cụ tìm kiếm Không có lối thoát khỏi sự đệ quy. Kể từ khi thông lệ được thiết lập để đo lường thẩm quyền của một trang web (tài liệu) bằng số lượng liên kết, các công cụ tìm kiếm đã rơi vào bẫy đệ quy và để chúng lang thang trong đó mãi mãi (điều này là chân thành). lời chúc tốt đẹp tác giả). “Trọng lượng” liên kết của một trang web bao gồm các phần “trọng lượng” nhỏ từ tất cả những liên kết đến nó. Để tính trọng số này cho A, được tham chiếu bởi B, C và D, bạn cần tính trọng lượng của chúng, trọng lượng này sẽ được truyền bởi tất cả các loại khác, trọng lượng của chúng cũng cần phải được tính toán... v.v. trên toàn bộ Mạng được tính đến trong công cụ tìm kiếm. Một vấn đề hoàn toàn đệ quy. Và bạn nói đó là một lý thuyết hoàn chỉnh. Thực hành thực tế nhất.

Xếp hạng trang đệ quy của Google

Những người tạo ra Google đã xuất bản thuật toán cơ bản của họ để tính PageRank từ lâu. Và cho dù nó có thay đổi như thế nào kể từ đó, cho dù có bao nhiêu cải tiến được thêm vào, thì cơ sở vẫn như cũ. Không có cách nào để biết trang B đang chuyển bao nhiêu PageRank dưới dạng liên kết đến trang A cho đến khi chúng tôi đếm xem trang B đã nhận được bao nhiêu PageRank từ tất cả các trang khác liên kết với nó và điều đó không thể biết được cho đến khi chúng tôi đếm PageRank của những trang đó... chúng ta sẽ tiếp tục chứ? Có lẽ không còn cần thiết nữa. Lại là Nữ hoàng nữa đệ quy .

Đệ quy là thuộc tính của một đối tượng để bắt chước chính nó. Một đối tượng có tính đệ quy nếu các phần của nó trông giống như toàn bộ đối tượng. Đệ quy được sử dụng rất rộng rãi trong toán học và lập trình:

  • cấu trúc dữ liệu:
    • một biểu đồ (đặc biệt là các cây và danh sách) có thể được xem dưới dạng tập hợp của một nút và một biểu đồ con (biểu đồ nhỏ hơn);
    • chuỗi bao gồm ký tự đầu tiên và một chuỗi con (chuỗi nhỏ hơn);
  • mẫu thiết kế, ví dụ: Một đối tượng trang trí có thể bao gồm các đối tượng khác cũng là đối tượng trang trí. McColm Smith đã nghiên cứu chi tiết các mẫu đệ quy, nêu bật một mẫu thiết kế chung trong cuốn sách của ông - Đệ quy;
  • các hàm đệ quy (thuật toán) tự gọi mình.

Bài viết dành cho việc phân tích độ phức tạp của các thuật toán đệ quy, cung cấp thông tin toán học cần thiết và xem xét các ví dụ. Ngoài ra, khả năng thay thế đệ quy bằng một vòng lặp, đệ quy đuôi, cũng được mô tả.

Ví dụ về thuật toán đệ quy

Thuật toán đệ quy luôn chia một bài toán thành các phần có cấu trúc giống như bài toán ban đầu nhưng đơn giản hơn. Để giải các bài toán con, một hàm được gọi đệ quy và kết quả của chúng được kết hợp theo một cách nào đó. Việc phân chia một nhiệm vụ chỉ xảy ra khi nó không thể được giải quyết ngay lập tức (nó quá phức tạp).

Ví dụ, nhiệm vụ xử lý một mảng thường có thể được đơn giản hóa thành việc xử lý các phần của nó. Việc phân chia thành các phần được thực hiện cho đến khi chúng trở thành sơ cấp, tức là. đủ đơn giản để có được kết quả mà không cần đơn giản hóa thêm.

Tìm một phần tử mảng

Bắt đầu; tìm kiếm (mảng, bắt đầu, kết thúc, phần tử); tìm kiếm một phần tử có phần tử giá trị trong mảng giữa các chỉ mục bắt đầu và kết thúc nếu bắt đầu > kết thúc:= sai; nếu không thì không tìm thấy phần tử nếu mảng = phần tử result:= true; phần tử được tìm thấy nếu không thì result:= search(array, started+1, end, element) end; kết quả trả về

Thuật toán chia mảng ban đầu thành hai phần - phần tử đầu tiên và mảng các phần tử còn lại. Có hai trường hợp đơn giản khi không cần phải tách - tất cả các phần tử đã được xử lý hoặc phần tử đầu tiên là phần tử đang được tìm kiếm.

Trong thuật toán tìm kiếm, mảng có thể được chia khác nhau (ví dụ: làm đôi), nhưng điều này sẽ không ảnh hưởng đến hiệu quả. Nếu mảng đã được sắp xếp thì nên chia nó làm đôi, bởi vì Ở mỗi bước, lượng dữ liệu được xử lý có thể giảm đi một nửa.

Tìm kiếm nhị phân trong một mảng

Tìm kiếm nhị phân được thực hiện trên một mảng được sắp xếp. Ở mỗi bước, phần tử được tìm kiếm sẽ được so sánh với giá trị nằm ở giữa mảng. Tùy thuộc vào kết quả so sánh, bên trái hoặc bên phải có thể bị "loại bỏ".

Bắt đầu; nhị phân_search(mảng, bắt đầu, kết thúc, phần tử); tìm kiếm phần tử có phần tử giá trị ; trong một mảng được sắp xếp theo mảng thứ tự tăng dần; giữa các chỉ số bắt đầu và kết thúc nếu bắt đầu > kết thúc; return false - không tìm thấy phần tử ở giữa:= (kết thúc + bắt đầu) div 2; tính chỉ số của phần tử ở giữa phần được xét của mảng nếu mảng = phần tử cuối; trả về true (tìm thấy phần tử) nếu mảng< element результат:= binary_search(array, mid+1, end, element) иначе результат:= binary_search(array, begin, mid, element) конец; вернуть результат

Tính số Fibonacci

Các số Fibonacci được xác định bởi một biểu thức lặp lại, tức là sao cho phép tính phần tử của nó được biểu thị từ các phần tử trước đó: \(F_0 = 0, F_1 ​​​​= 1, F_n = F_(n-1) + F_(n-2), n > 2\).

Bắt đầu; fibonacci(số) nếu số = 0 kết thúc; trả về 0 nếu số = 1 tận cùng; return 1 fib_1:= fibonacci(number-1) fib_2:= fibonacci(number-2) result:= fib_1 + fib_2 end; kết quả trả về

Sắp xếp nhanh chóng

Thuật toán sắp xếp nhanh chóngở mỗi bước, chọn một trong các phần tử (tham chiếu) và liên quan đến nó chia mảng thành hai phần, được xử lý đệ quy. Các phần tử nhỏ hơn phần hỗ trợ được đặt ở một phần và phần còn lại được đặt ở phần kia.

Sơ đồ thuật toán sắp xếp nhanh

Hợp nhất sắp xếp

Thuật toán sắp xếp hợp nhất dựa trên khả năng liên kết nhanh sắp xếp các mảng (hoặc danh sách) sao cho kết quả được sắp xếp theo thứ tự. Thuật toán chia mảng ban đầu thành hai phần ngẫu nhiên(thường là một nửa), sắp xếp đệ quy chúng và nối kết quả. Phép chia xảy ra miễn là kích thước mảng lớn hơn một, bởi vì một mảng trống và một mảng gồm một phần tử luôn được sắp xếp.

Sơ đồ khối sắp xếp hợp nhất

Ở mỗi bước hợp nhất, phần tử thô đầu tiên từ cả hai danh sách sẽ được chọn. Các phần tử được so sánh và phần tử nhỏ nhất được thêm vào kết quả và được đánh dấu là đã xử lý. Việc hợp nhất diễn ra cho đến khi một trong các danh sách trống.

Bắt đầu; hợp nhất(Array1, Size1, Array2, Size2); các mảng ban đầu được sắp xếp; kết quả là một mảng có thứ tự có độ dài Size1+Size2 i:= 0, j:= 0 Eternal_loop nếu i >= Size1 thêm các phần tử từ j vào Size2 của mảng Array2 vào cuối kết quả thoát khỏi vòng lặp nếu j >= Size2 thêm các phần tử từ i vào Size1 của mảng Array1 vào cuối kết quả thoát khỏi vòng lặp nếu Array1[i]< Array2[j] результат := Array1[i] i:= i + 1 иначе (если Array1[i] >= Array2[j]) kết quả := Array2[j] j:= j + 1 kết thúc; kết quả trả về

Phân tích các thuật toán đệ quy

Khi tính độ phức tạp của các lần lặp và số lượng của chúng trong các trường hợp xấu nhất, tốt nhất và trung bình. Tuy nhiên, sẽ không thể áp dụng cách tiếp cận này cho hàm đệ quy, bởi vì kết quả sẽ là một mối quan hệ tái diễn. Ví dụ: đối với hàm tìm kiếm một phần tử trong mảng:

\(
\begin(phương trình*)
T^(tìm kiếm)_n = \begin(case)
\mathcal(O)(1) \quad &\text($n = 0$) \\
\mathcal(O)(1) + \mathcal(O)(T^(search)_(n-1)) \quad &\text($n > 0$)
\end(trường hợp)
\end(phương trình*)
\)

Các mối quan hệ lặp lại không cho phép chúng tôi đánh giá độ phức tạp - chúng tôi không thể so sánh chúng một cách đơn giản và do đó so sánh tính hiệu quả của các thuật toán tương ứng. Cần phải có được một công thức mô tả mối quan hệ truy hồi - một cách phổ quátĐể làm điều này là chọn một công thức bằng phương pháp thay thế, sau đó chứng minh sự tương ứng của công thức với mối quan hệ bằng phương pháp quy nạp toán học.

Phương pháp thay thế (lặp)

Nó bao gồm việc thay thế tuần tự phần lặp lại trong một biểu thức để thu được các biểu thức mới. Việc thay thế được thực hiện cho đến khi có thể bắt được Nguyên tắc chung và biểu diễn nó dưới dạng công thức không hồi quy. Ví dụ: để tìm kiếm một phần tử trong mảng:

\(
T^(tìm kiếm)_n = \mathcal(O)(1) + \mathcal(O)(T^(search)_(n-1)) =
2\times\mathcal(O)(1) + \mathcal(O)(T^(search)_(n-2)) =
3\times\mathcal(O)(1) + \mathcal(O)(T^(search)_(n-3))
\)

Chúng ta có thể giả định rằng \(T^(search)_n = T^(search)_(n-k) + k\times\mathcal(O)(1)\), nhưng sau đó \(T^(search)_n = T^ (tìm kiếm)_(0) + n\times\mathcal(O)(1) = \mathcal(O)(n)\).

Chúng tôi đã rút ra công thức, nhưng bước đầu tiên chứa một giả định, tức là. không có bằng chứng nào về sự tương ứng của công thức với biểu thức truy hồi - phương pháp quy nạp toán học cho phép chúng ta thu được bằng chứng.

Phương pháp quy nạp toán học

Cho phép bạn chứng minh tính đúng đắn của một phát biểu nào đó (\(P_n\)), gồm 2 bước:

  1. bằng chứng của tuyên bố cho một hoặc nhiều trường hợp đặc biệt \(P_0, P_1, …\);
  2. từ chân lý \(P_n\) (giả thuyết quy nạp) và các trường hợp đặc biệt suy ra chứng minh \(P_(n+1)\).

Hãy để chúng tôi chứng minh tính đúng đắn của giả định được đưa ra khi ước tính độ phức tạp của hàm tìm kiếm (\(T^(search)_n = (n+1)\times\mathcal(O)(1)\)):

  1. \(T^(search)_(1) = 2\times\mathcal(O)(1)\) đúng với điều kiện (có thể được thay thế vào công thức truy hồi ban đầu);
  2. hãy giả định sự thật \(T^(search)_n = (n+1)\times\mathcal(O)(1)\);
  3. chúng ta cần chứng minh rằng \(T^(search)_(n+1) = ((n+1)+1)\times\mathcal(O)(1) = (n+2)\times\mathcal(O ) (1)\);
    1. hãy thay thế \(n+1\) vào quan hệ truy hồi: \(T^(search)_(n+1) = \mathcal(O)(1) + T^(search)_n\);
    2. ở phía bên phải của biểu thức có thể thực hiện thay thế dựa trên giả thuyết quy nạp: \(T^(search)_(n+1) = \mathcal(O)(1) + (n+1)\times \mathcal(O)(1) = (n+2)\times\mathcal(O)(1)\);
    3. tuyên bố đã được chứng minh.

Thông thường, việc chứng minh như vậy là một quá trình tốn nhiều công sức, nhưng việc xác định một mẫu bằng phương pháp thay thế thậm chí còn khó khăn hơn. Về vấn đề này, cái gọi là phương pháp chung.

Phương pháp tổng quát (cơ bản) để giải các quan hệ truy hồi

Phương pháp chung không phổ biến; ví dụ, nó không thể được sử dụng để ước tính độ phức tạp của thuật toán tính số Fibonacci trên. Tuy nhiên, nó áp dụng cho mọi trường hợp sử dụng phương pháp chia để trị:

\(T_n = a\cdot T(\frac(n)(b))+f_n; a, b = const, a \geq 1, b > 1, f_n > 0, \forall n\).

Các phương trình loại này thu được nếu bài toán ban đầu được chia thành các nhiệm vụ con, mỗi nhiệm vụ xử lý các phần tử \(\frac(n)(b)\). \(f_n\) là độ phức tạp của các thao tác chia bài toán thành các phần và kết hợp lời giải. Ngoài loại mối quan hệ, phương thức chung áp đặt các hạn chế đối với hàm \(f_n\), phân biệt ba trường hợp:

  1. \(\exists \varepsilon > 0: f_n = \mathcal(O)(n^(\log_b a - \varepsilon)) \Rightarrow T_n = \Theta(n^(\log_b a))\);
  2. \(f_n = \Theta(n^(\log_b a)) \Rightarrow T_n = \Theta(n^(\log_b a) \cdot \log n)\);
  3. \(\tồn tại \varepsilon > 0, c< 1: f_n = \Omega(n^{\log_b a + \varepsilon}), f_{\frac{n}{b}} \leq c \cdot f_n \Rightarrow T_n = \Theta(f_n)\).

Tính đúng đắn của các phát biểu trong từng trường hợp đã được chứng minh một cách hình thức. Nhiệm vụ phân tích một thuật toán đệ quy bây giờ là xác định trường hợp của định lý chính tương ứng với quan hệ truy hồi.

Phân tích thuật toán tìm kiếm nhị phân

Thuật toán chia dữ liệu nguồn thành 2 phần (b = 2), nhưng chỉ xử lý một trong số chúng (a = 1), \(f_n = 1\). \(n^(\log_b a) = n^(\log_2 1) = n^0 = 1\). Hàm phân chia nhiệm vụ và sắp xếp kết quả tăng trưởng với tốc độ tương đương với \(n^(\log_b a)\), nghĩa là cần sử dụng trường hợp thứ hai của định lý:

\(T^(binarySearch)_n = \Theta(n^(\log_b a) \cdot \log n) = \Theta(1 \cdot \log n) = \Theta(\log n)\).

Phân tích thuật toán tìm kiếm

Phân chia hàm đệ quy vấn đề ban đầu mỗi nhiệm vụ con (a = 1), dữ liệu được chia thành một phần (b = 1). Chúng ta không thể sử dụng định lý chính để phân tích thuật toán này, bởi vì điều kiện \(b > 1\) không được thỏa mãn.

Để thực hiện phân tích, có thể sử dụng phương pháp thay thế hoặc lý do sau: mỗi lệnh gọi đệ quy làm giảm một chiều của dữ liệu đầu vào, nghĩa là sẽ có tổng cộng n phần, mỗi phần có độ phức tạp \(\mathcal( O)(1)\). Khi đó \(T^(search)_n = n \cdot \mathcal(O)(1) = \mathcal(O)(n)\).

Phân tích thuật toán sắp xếp hợp nhất

Dữ liệu nguồn được chia thành hai phần, cả hai phần đều được xử lý: \(a = 2, b = 2, n^(\log_b a) = n\).

Khi xử lý một danh sách, phép chia có thể yêu cầu các phép toán \(\Theta(n)\), trong khi đối với một mảng thì cần có thời gian không đổi (\(\Theta(1)\)). Tuy nhiên, trong mọi trường hợp, \(\Theta(n)\) sẽ được dùng để kết nối các kết quả, vì vậy \(f_n = n\).

Trường hợp thứ hai của định lý được sử dụng: \(T^(mergeSort)_n = \Theta(n^(\log_b a) \cdot \log n) = \Theta(n \cdot \log n)\).

Phân tích cường độ lao động của việc sắp xếp nhanh chóng

TRONG kịch bản hay nhất mảng ban đầu được chia thành hai phần, mỗi phần chứa một nửa dữ liệu gốc. Việc chia sẽ yêu cầu n hoạt động. Độ phức tạp của việc soạn kết quả phụ thuộc vào cấu trúc dữ liệu được sử dụng - đối với mảng \(\mathcal(O)(n)\), đối với danh sách liên kết \(\mathcal(O)(1)\). \(a = 2, b = 2, f_n = b\), có nghĩa là độ phức tạp của thuật toán sẽ giống như độ phức tạp của sắp xếp hợp nhất: \(T^(quickSort)_n = \mathcal(O)(n \ cdot \log n)\ ).

Tuy nhiên, trong trường hợp xấu nhất, mức tối thiểu hoặc phần tử tối đa mảng. Khi đó \(b = 1\), có nghĩa là chúng ta lại không thể sử dụng định lý chính. Tuy nhiên, chúng ta biết rằng trong trường hợp này, n lệnh gọi đệ quy sẽ được thực hiện, mỗi lệnh gọi chia mảng thành các phần (\(\mathcal(O)(n)\)) - có nghĩa là độ phức tạp của thuật toán \(T^( quickSort)_n = \mathcal(O)(n^2)\).

Khi phân tích sắp xếp nhanh bằng cách thay thế, người ta cũng sẽ phải xem xét riêng các trường hợp tốt nhất và xấu nhất.

Đệ quy đuôi và vòng lặp

Việc phân tích độ phức tạp của hàm đệ quy phức tạp hơn nhiều so với việc đánh giá các vòng lặp, nhưng lý do chính khiến vòng lặp được ưa chuộng hơn là do chi phí gọi hàm cao.

Sau cuộc gọi quyền kiểm soát được chuyển giao một chức năng khác. Để chuyển điều khiển, chỉ cần thay đổi giá trị của thanh ghi bộ đếm chương trình, trong đó bộ xử lý lưu trữ số lệnh hiện đang được thực hiện - theo cách tương tự, điều khiển được chuyển đến các nhánh của thuật toán, chẳng hạn như khi sử dụng điều hành có điều kiện. Tuy nhiên, một cuộc gọi không chỉ là sự chuyển giao quyền điều khiển, bởi vì sau khi hàm được gọi hoàn thành việc tính toán, nó phải kiểm soát trở lạiđến thời điểm cuộc gọi được thực hiện, đồng thời khôi phục giá trị của các biến cục bộ đã tồn tại ở đó trước cuộc gọi.

Để thực hiện hành vi này, một ngăn xếp (ngăn xếp cuộc gọi) được sử dụng - số lệnh trả về và thông tin về các biến cục bộ được đặt trong đó. Ngăn xếp không phải là vô hạn, vì vậy thuật toán đệ quy có thể dẫn đến tình trạng tràn của nó, trong mọi trường hợp, làm việc với nó có thể mất một khoảng thời gian đáng kể.

Trong một số trường hợp, khá dễ dàng để thay thế hàm đệ quy bằng một vòng lặp, chẳng hạn như những gì đã thảo luận ở trên. Trong một số trường hợp cần nhiều hơn sáng tạo, nhưng thường thì việc thay thế như vậy là có thể. Ngoài ra, có một loại đệ quy đặc biệt trong đó lệnh gọi đệ quy được thực hiện hoạt động cuối cùngđược thực hiện bởi chức năng. Rõ ràng, trong trường hợp này chức năng gọi điện sẽ không thay đổi kết quả theo bất kỳ cách nào, điều đó có nghĩa là không có ích gì khi trả lại quyền kiểm soát. Như là đệ quy được gọi là đệ quy đuôi- trình biên dịch tự động thay thế nó bằng một vòng lặp.

Thường thì việc thực hiện đệ quy đuôi sẽ giúp ích phương pháp tham số tích lũy, bao gồm việc thêm chức năng của một đối số tích lũy bổ sung trong đó kết quả được tích lũy. Hàm thực hiện các phép tính trên bộ tích lũy trước lệnh gọi đệ quy. Một ví dụ tốt Sử dụng kỹ thuật này là chức năng tính giai thừa:
\(fact_n = n \cdot Fact(n-1) \\
Fact_3 = 3 \cdot Fact_2 = 3 \cdot (2 \cdot Fact_1) = 3\cdot (2 \cdot (1 \cdot Fact_0)) = 6 \\
Fact_n = FactTail_(n, 1) \\
\\
FactTail_(n, bộ tích lũy) = FactTail(n-1, bộ tích lũy \cdot n)\\
FactTail_(3, 1) = FactTail_(2, 3) = FactTail_(1, 6) = FactTail_(0, 6) = 6
\)

Một ví dụ phức tạp hơn, hãy xem xét hàm tính số Fibonacci. Hàm chính gọi hàm phụ sử dụng phương thức tham số tích lũy, truyền dưới dạng đối số giá trị ban đầu một bộ lặp và hai bộ tích lũy (hai số Fibonacci trước đó).

Bắt đầu; fibonacci(số) trả về fibonacci(số, 1, 1, 0) kết thúc bắt đầu; fibonacci(number, iterator, fib1, fib2) if iterator == number return fib1 return fibonacci(number, iterator + 1, fib1 + fib2, fib1) end

Hàm có tham số tích lũy trả về kết quả tích lũy nếu số lượng số được chỉ định được tính toán, nếu không, hàm sẽ tăng bộ đếm, tính số Fibonacci mới và thực hiện lệnh gọi đệ quy. Trình biên dịch tối ưu hóa có thể phát hiện ra rằng kết quả của lệnh gọi hàm được truyền không thay đổi đến đầu ra của hàm và thay thế nó bằng một vòng lặp. Kỹ thuật này đặc biệt phù hợp trong các ngôn ngữ lập trình hàm và logic, bởi vì trong đó người lập trình không thể sử dụng rõ ràng các cấu trúc tuần hoàn.

Văn học

  1. Máy chủ Qt đa luồng. Nhóm chủ đề. Mẫu trang trí [ Tài nguyên điện tử] - chế độ truy cập: https://site/archives/1390. Ngày truy cập: 21/02/2015.
  2. Jason McColm Smith: Chuyển giới. từ tiếng Anh - M.: LLC “I.D. Williams”, 2013. - 304 tr.
  3. Skiena S. Thuật toán. Hướng dẫn phát triển.-tái bản lần 2: trans. từ tiếng Anh-SPb.: BHV-Petersburg, 2011.-720 trang.: ill.
  4. Vasiliev V. S. Phân tích độ phức tạp của thuật toán. Ví dụ [Tài nguyên điện tử] - chế độ truy cập: https://trang web/lưu trữ/1660. Ngày truy cập: 21/02/2015.
  5. A. Aho, J. Hopcroft, J. Ullman, Cấu trúc dữ liệu và thuật toán, M., Williams, 2007.
  6. Miller, R. Tuần tự và thuật toán song song: Cách tiếp cận chung/ R. Miller, L. Boxer; làn đường từ tiếng Anh - M.: BINOM. Phòng thí nghiệm tri thức, 2006. - 406 tr.
  7. Sergievsky G.M. Lập trình chức năng và logic: sách giáo khoa. cẩm nang dành cho học sinh cao hơn sách giáo khoa cơ sở / G.M. Sergievsky, N.G. Volchenkov. - M.: Trung tâm xuất bản "Học viện", 2010.- 320 tr.

Các chương trình con trong Pascal có thể tham chiếu đến chính chúng. Loại điều trị này được gọi là đệ quy .

Để việc truy cập như vậy không phải là vô tận, văn bản của chương trình con phải chứa một điều kiện để đạt được điều kiện mà việc truy cập thêm vào chương trình con không xảy ra.

Ví dụ .

Chúng ta hãy xem một câu đố toán học trong cuốn sách “Trò chơi lập trình và câu đố” của J. Arsac.

Hãy xây dựng một dãy số như sau: lấy số nguyên i>1. Số hạng tiếp theo trong dãy bằng i/2 nếu tôi chẵn và 3 i+1 nếu tôi lẻ. Nếu i=1 thì chuỗi dừng lại.

Về mặt toán học, tính hữu hạn của dãy, bất kể i ban đầu, vẫn chưa được chứng minh, nhưng trên thực tế, dãy luôn dừng.

Việc sử dụng đệ quy giúp giải quyết vấn đề mà không cần sử dụng vòng lặp, cả trong chương trình chính và thủ tục.

Chương trình ví dụ sử dụng đệ quy

Chương trình Arsac;
Var đầu tiên: từ;
Thủ tục posledov (i: word);
Bắt đầu
Writeln(i);
Nếu i=1 thì thoát;
Nếu lẻ(i) thì posledov(3*i+1) else posledov(i div 2);
Kết thúc;
Bắt đầu
Write("nhập giá trị đầu tiên"); readln(đầu tiên);
Posledov (đầu tiên);
Đọc;
Kết thúc.

Người lập trình phát triển một chương trình, rút ​​gọn vấn đề ban đầu thành những vấn đề đơn giản hơn. Trong số các nhiệm vụ này có thể có nhiệm vụ gốc nhưng ở dạng đơn giản hóa. Ví dụ, để tính F(N), bạn có thể cần tính F(N-1). Nói cách khác, một phần của thuật toán tính một hàm sẽ là phép tính của hàm đó.

Một thuật toán là một phần riêng của nó được gọi là đệ quy. Thông thường cơ sở của thuật toán như vậy là định nghĩa đệ quy.

N! = (N-1)!* N, nếu N=0 thì N!= 1

Bất kỳ định nghĩa đệ quy nào cũng bao gồm hai phần. Một phần xác định một khái niệm thông qua nó, phần còn lại thông qua các khái niệm khác.

Ví dụ về thuật toán đệ quy

2n= 2 n-1*2, nếu n=0 thì 2 n= 1

Thủ tục là đệ quy, nếu nó đề cập đến chính nó một cách trực tiếp hoặc gián tiếp (thông qua các thủ tục khác).

Lưu ý rằng với truy cập gián tiếp, tất cả các thủ tục trong chuỗi đều có tính đệ quy.

Mọi điều nói về thủ tục đều áp dụng hoàn toàn cho các hàm.

Ví dụ về hàm giai thừa đệ quy

Hàm giai thừa(N: số nguyên): longint;
Bắt đầu
Nếu N= 0 thì
Giai thừa:= 1
Khác Giai thừa:= giai thừa(N-1) * N
Kết thúc;

Đệ quy từ bên trong

Điều này có vẻ đáng ngạc nhiên, nhưng bản thân việc gọi một thủ tục cũng không khác gì việc gọi một thủ tục khác. Điều gì xảy ra nếu một thủ tục gọi một thủ tục khác? TRONG phác thảo chung tiếp theo:

  • các tham số được truyền cho thủ tục được đặt trong bộ nhớ (nhưng không phải các tham số biến!);
  • các giá trị của các biến nội bộ của thủ tục gọi được lưu trữ ở nơi khác trong bộ nhớ;
  • địa chỉ trả về của thủ tục gọi được ghi nhớ;
  • điều khiển được chuyển sang thủ tục được gọi.

Nếu một thủ tục được gọi lại từ một thủ tục khác hoặc từ chính nó, cùng một mã sẽ được thực thi, nhưng nó sẽ hoạt động với các giá trị tham số và biến nội bộ khác nhau. Đây là những gì làm cho đệ quy có thể.

Để thủ tục đệ quy Power(X, N, Y) nâng số X lên lũy thừa N và trả về kết quả Y .

Một ví dụ về thủ tục đệ quy nâng một số lên lũy thừa

Thủ tục Power (X: real; N: số nguyên; var Y: real);
Bắt đầu
Nếu N=0 thì
Y:= 1
Khác Bắt Đầu Nguồn(X, N-1,Y);
Y:= Y*X;
Kết thúc ;
Kết thúc ;

Hãy theo dõi trạng thái bộ nhớ trong quá trình thực hiện lệnh gọi thủ tục này Power(5,3, Y). Mũi tên “->” nghĩa là nhập thủ tục, mũi tên “

Số lượng bản sao của các biến đồng thời trong bộ nhớ được gọi là độ sâu đệ quy . Như có thể thấy từ ví dụ, đầu tiên nó phát triển và sau đó co lại.

Tóm tắt.

  • Một thủ tục hoặc hàm gọi chính nó được gọi là đệ quy;
  • Để hoàn thành quá trình đệ quy, thuật toán của hàm (thủ tục) đệ quy phải có điều kiện đảm bảo hoàn thành ngay lập tức hàm (thủ tục).

Hãy viết chương trình tính hàm được xác định như sau:

F(1)=1; F(2n)=F(n); F(2n+1)=F(n)+F(n+1)

Giải pháp: Từ định nghĩa, rõ ràng rằng việc tính toán một hàm từ một đối số sẽ rút gọn thành việc tính toán cùng một hàm từ một đối số nhỏ hơn và quá trình rút gọn đối số sẽ tiếp tục cho đến khi đối số là một. Giá trị của hàm từ sự thống nhất được xác định.

Do đó, công việc của thuật toán sẽ bao gồm một số bước nhất định, tại mỗi bước sẽ có hai hành động được thực hiện:

  • Xác định xem một đối số là chẵn hay lẻ. Việc lựa chọn công thức tính phụ thuộc vào điều này;
  • Thực hiện các phép tính. Trong thực tế, điều này sẽ dẫn đến việc xác định một đối số hàm mới.
Ví dụ về chương trình đánh giá hàm đệ quy

Chương trình mồi;
Sử dụngscrt;
Var
N, a: số nguyên;
Hàm f(n:số nguyên): số nguyên;
Bắt đầu
Nếu n = 1 thì
f:=1 (điều kiện kết thúc đệ quy)
Khác
Bắt đầu
Nếu lẻ (n)
sau đó bắt đầu
n:= n div 2;
f:=f(n)+f(n+1)
kết thúc
bắt đầu khác
n:= n div 2;
f:=f(n)
kết thúc;
kết thúc ;
kết thúc ;
clrscr;
write("Nhập số - ");
đọcln(n);
a:=f(n);
write("kết quả – ", a);
kết thúc.

Chương trình xây dựng bông tuyết đệ quy

Viết chương trình tạo ảnh trên màn hình:

Hình ảnh được dựng theo quy tắc sau: dựng một đường tròn có bán kính r cho trước. Sau đó, tại các điểm đối xứng đường kính của đường tròn (x- r và x+ r), một đường tròn có bán kính nhỏ hơn (r = 3 r/5) lại được dựng. Đối với mỗi đường tròn nhỏ hơn, một đường tròn có bán kính nhỏ hơn lại được dựng tại các điểm đối xứng đường kính, v.v., cho đến khi bán kính giảm xuống 10.

chương trình tái diễn;
sử dụng đồ thị;
var x,y,r,d,m:số nguyên;
var i: số nguyên;
bắt đầu
nếu r đường tròn(x,y,r);
đối với i:=1 đến 1000 do; (chỉ là một vòng lặp trễ)
ris(x+r,y,r*3 div 5);
ris(x-r,y,r*3 div 5);
kết thúc ;
bắt đầu (bắt đầu chương trình chính)
d:=phát hiện;
x:=320;
y:=240;
r:=120;
ris(x,y,r);
đọc;
kết thúc.

Như có thể thấy từ hình, các đoạn tương tự được lặp lại ở đây một lần nữa. Việc xây dựng được thực hiện như sau: trên một đường tròn có bán kính r cho trước, lấy 6 điểm cách đều nhau (bắt đầu từ góc 0 0, với bước ?/3), bán kính được vẽ từ mỗi điểm đến tâm của vòng tròn. Khi đó mỗi điểm này đóng vai trò là tâm của một đường tròn mới, nhỏ hơn có bán kính r=2 r/5. Trên mỗi vòng tròn nhỏ hơn, lại lấy 6 điểm cách đều nhau, từ đó bán kính được xây dựng đến tâm, v.v., cho đến khi bán kính nhỏ hơn hoặc bằng 1.

chương trình tuyết;
sử dụng đồ thị, crt;
var
x,y,r,d,m:số nguyên;
thủ tục ris(x,y,r:số nguyên);
var
x1,y1,t:số nguyên;
bắt đầu
nếu r cho t:=0 đến 6 thì làm
bắt đầu
x1:=x+trunc(r*cos(t*pi/3));
y1:=y+trunc(r*sin(t*pi/3));
dòng(x,y,x1,y1);
ris(x1,y1,r*2 div 5);
độ trễ (500);
kết thúc;
kết thúc;
bắt đầu
d:=phát hiện;
initgraph(d,m,"e:\bp\bgi");
x:=320;
y:=240;
r:=80;
ris(x,y,r);
đọc;
kết thúc.

Một ví dụ về "Đường cong rồng".

Hãy xem một ví dụ về cách giải một bài toán kinh điển khác: “Đường cong rồng”.

Hình ảnh đường cong Rồng trông như thế này:

Rất đẹp phải không nào. Chúng ta hãy tìm hiểu làm thế nào có được đường cong này.

Chúng ta hãy lấy một dải giấy dài gấp đôi rồi xoay nó 90 độ, nếu nhìn từ bên cạnh, bạn sẽ thấy một đường đứt nét gồm hai phần vuông góc: xem. cơm. MỘT. Bây giờ hãy gấp dải giấy làm đôi hai lần và cũng xoay nó 90 độ hai lần như minh họa trong hình. cơm. b. Chúng ta thu được một đường gãy gồm bốn đoạn và góc giữa các đoạn liền kề là 90. Cuối cùng, nếu việc gấp và mở dải được thực hiện ba lần thì kết quả sẽ là hình minh họa trong cơm. V. Bằng cách tiếp tục quá trình này, bạn có thể có được một đường cong tương tự như đường cong được hiển thị trong cơm. 1. Đường cong kỳ lạ này được gọi là đường cong rồng. Phương pháp xây dựng cho thấy nó không có nút giao tự thân.

Đường cong rồng được mô tả lần đầu tiên trong tài liệu phổ thông trên tạp chí Scientific American vào năm 1967. Một ghi chú về nó xuất hiện trong chuyên mục “Trò chơi toán học” do Martin Gardner viết. Ban đầu, tên đầy đủ của đường cong được sử dụng - “Rồng Harter-Hateway”, do người sáng lập đặt cho nó fractal máy tính hình học Benoit Mandelbrot, người đặt tên cho tập hợp nổi tiếng này. Sau đó họ bắt đầu nói chuyện đơn giản về đường cong của rồng. Ở trên chúng tôi đã mô tả một trong các thuật toán để xây dựng đường cong. Theo ý kiến ​​của chúng tôi, nó hơi khó hiểu (mặc dù khá đơn giản để thực hiện). Đây là mô tả về thuật toán xây dựng đường cong, gần giống với thuật toán được Martin Gardner sử dụng.

Hãy coi đoạn ngang là đường cong rồng có bậc 0. Chia đoạn thẳng làm đôi và tạo một góc vuông trên đó, như minh họa trong cơm. MỘT).

Chúng ta thu được đường cong rồng bậc nhất. Trên các cạnh của góc vuông, chúng ta lại dựng các góc vuông ( cơm. b).

Trong trường hợp này, đỉnh của góc thứ nhất luôn ở bên phải khi nhìn từ điểm A (điểm bắt đầu của đường cong) dọc theo đoạn đầu tiên của đường cong và các hướng tạo các đỉnh của các góc còn lại xen kẽ nhau. TRÊN cơm. c) và d) lần lượt thể hiện các đường cong rồng bậc ba và bậc bốn.

chương trình rồng;
sử dụng đồ thị;
var k,d,m:số nguyên;
thủ tục ris(x1,y1,x2,y2,k:số nguyên);
var xn,yn:integer;
bắt đầu
nếu k>0 thì
bắt đầu
xn:=(x1+x2) div 2 +(y2-y1) div 2;
yn:=(y1+y2) div 2 -(x2-x1) div 2;
ris(x1,y1,xn,yn,k-1);
ris(x2,y2,xn,yn,k-1);
kết thúc
dòng khác bắt đầu (x1,y1,x2,y2); kết thúc;
kết thúc;
bắt đầu
readln(k); (đặt thứ tự của đường cong)
d:=phát hiện;
initgraph(d,m,"e:\bp\bgi");
ris(200,300,500,300,k);
đọc;
kết thúc.

Đến Đại học Quốc gia Đông Ukraine mang tên Vladimir Dahl

đệ quy

Khoa học máy tính và công nghệ máy tính

© Veligura A.V., Khoa Điều khiển học Kinh tế, 2004

Đệ quy - phương pháp mạnh mẽ lập trình, cho phép bạn chia một vấn đề thành các phần ngày càng nhỏ hơn cho đến khi chúng trở nên nhỏ đến mức giải pháp cho các vấn đề con này được rút gọn thành một tập hợp các thao tác đơn giản.

Khi bạn đã có được kinh nghiệm về đệ quy, bạn sẽ thấy nó ở khắp mọi nơi. Nhiều lập trình viên gần đây đã thành thạo đệ quy và bắt đầu sử dụng nó trong những tình huống không cần thiết và đôi khi có hại.

Đệ quy là gì?

Đệ quy xảy ra khi một hàm hoặc chương trình con gọi chính nó. Thẳng đệ quy(đệ quy trực tiếp) trông giống như thế này:

Hàm Giai thừa(num As Long) As Long

Giai thừa = num * Giai thừa(num - 1)

Khi đệ quy gián tiếp(đệ quy gián tiếp) một thủ tục đệ quy gọi một thủ tục khác, thủ tục này lần lượt gọi thủ tục đầu tiên:

Ping phụ riêng tư(num dưới dạng số nguyên)

Riêng Sub Pong(num As Integer)

Đệ quy rất hữu ích khi giải các bài toán được chia thành nhiều bài toán con một cách tự nhiên, mỗi bài toán đó phức tạp hơn. trường hợp đơn giản nhiệm vụ ban đầu. Bạn có thể hình dung một cái cây như một “thân cây” với hai cây nhỏ hơn trên đó. Sau đó bạn có thể viết thủ tục đệ quyđể vẽ cây:

Cây rút phụ riêng tư()

Vẽ "thân cây"

Vẽ một cái cây nhỏ hơn xoay -45 độ

Vẽ một cái cây nhỏ hơn xoay 45 độ

Mặc dù đệ quy có thể làm cho một số vấn đề dễ hiểu hơn nhưng nhìn chung mọi người không nghĩ theo kiểu đệ quy. Họ thường cố gắng chia các nhiệm vụ phức tạp thành các nhiệm vụ nhỏ hơn để có thể hoàn thành lần lượt từng nhiệm vụ cho đến khi hoàn thành. Ví dụ: để vẽ hàng rào, bạn có thể bắt đầu từ cạnh trái và tiếp tục sang bên phải cho đến khi hoàn thành. Bạn có thể không nghĩ đến khả năng tô màu đệ quy nửa bên trái của hàng rào trước, sau đó tô màu đệ quy nửa bên phải khi thực hiện một nhiệm vụ như thế này.

Để suy nghĩ đệ quy, bạn cần chia một bài toán thành các bài toán con, sau đó có thể chia các bài toán con này thành các bài toán con nhỏ hơn. Tại một thời điểm nào đó, các nhiệm vụ phụ trở nên đơn giản đến mức chúng có thể được thực hiện trực tiếp. Khi các nhiệm vụ phụ được hoàn thành, các nhiệm vụ phụ lớn hơn bao gồm chúng cũng sẽ được hoàn thành. Nhiệm vụ ban đầu sẽ được hoàn thành khi tất cả các nhiệm vụ phụ của nó được hoàn thành.

    1. Sự nguy hiểm của sự đệ quy

      1. Đệ quy vô hạn

Mối nguy hiểm rõ ràng nhất của đệ quy là đệ quy vô hạn. Nếu thuật toán được xây dựng không chính xác, hàm có thể thiếu điều kiện dừng đệ quy và thực thi vô tận. Cách dễ nhất để mắc lỗi này là quên kiểm tra điều kiện dừng, như được thực hiện trong phiên bản sai sau đây của hàm giai thừa. Vì hàm không kiểm tra xem đã đạt đến điều kiện dừng đệ quy hay chưa nên nó sẽ tự gọi chính nó không ngừng.

Hàm riêng BadFactorial(num As Integer) As Integer

BadFactorial = num * BadFactorial (num - 1)

Một hàm cũng có thể gọi chính nó vô thời hạn nếu điều kiện dừng không chấm dứt mọi thứ những cách có thểđệ quy. Trong phiên bản lỗi sau đây của hàm giai thừa, hàm sẽ tự gọi vô tận nếu giá trị đầu vào không phải là số nguyên hoặc nhỏ hơn 0. Các giá trị này không phải là giá trị đầu vào hợp lệ cho hàm giai thừa, vì vậy chương trình sử dụng các giá trị đầu vào của hàm này có thể cần phải kiểm tra. Tuy nhiên, sẽ tốt hơn nếu hàm này tự thực hiện việc kiểm tra này.

Hàm riêng BadFactorial2(num As Double) Là Double

BadFactory2 = 1

BadFactorial2 = num * BadFactorial2(num-1)

Phiên bản tiếp theo của hàm Fibonacci được bổ sung thêm ví dụ phức tạp. Điều kiện dừng đệ quy của nó chỉ dừng một vài đường đệ quy và gặp vấn đề tương tự như BadFactorial2 nếu giá trị đầu vào là âm hoặc không nguyên.

Hàm riêng BadFib(num As Double) As Double

BadFib = BadPib(num - 1) + BadFib (num - 2)

Vấn đề cuối cùng với đệ quy vô hạn là "vô hạn" thực sự có nghĩa là "cho đến khi hết dung lượng ngăn xếp". Ngay cả các thủ tục đệ quy được viết tốt đôi khi cũng dẫn đến tình trạng tràn ngăn xếp và bị sập. Hàm sau đây tính tổng của N + (N - 1) + ... + 2 +1, khiến không gian ngăn xếp bị cạn kiệt đối với các giá trị lớn của N. Giá trị N cao nhất có thể mà chương trình vẫn hoạt động tùy thuộc vào cấu hình máy tính của bạn.

Chức năng riêng tư BigAdd(N As Double) As Double

Nếu N<= 1 Then

BigAdd=N + BigAdd(N - 1)

Chương trình BigAdd trên đĩa mẫu minh họa thuật toán này. Kiểm tra giá trị đầu vào bạn có thể nhập vào chương trình này lớn đến mức nào trước khi gây ra tình trạng tràn ngăn xếp trên máy tính của bạn.

Chức năng: đưa ra đệ quy một hàm trong định nghĩa của nó chứa chính nó; cụ thể, một hàm được xác định bởi công thức truy hồi là hàm đệ quy. Do đó, trong một biểu thức, bạn có thể đưa ra vô số cách để tính hàm, xác định nhiều đối tượng thông qua chính mình bằng cách sử dụng các định nghĩa riêng đã chỉ định trước đó.

Dữ liệu

Cấu trúc element_of_list ( element_of_list * next; /* tham chiếu đến phần tử tiếp theo cùng loại */ dữ liệu int; /* Một số dữ liệu */ ) ;

Cấu trúc đệ quy của dữ liệu thường quy định việc sử dụng đệ quy để xử lý dữ liệu này.

Trong vật lý

Một ví dụ kinh điển về đệ quy vô hạn là hai tấm gương đặt đối diện nhau: chúng tạo thành hai hành lang phản xạ giảm dần của gương.

Một ví dụ khác về đệ quy vô hạn là tác dụng tự kích thích (phản hồi dương) trong các mạch khuếch đại điện tử, khi tín hiệu từ đầu ra đi đến đầu vào, được khuếch đại, quay trở lại đầu vào của mạch và được khuếch đại trở lại. Bộ khuếch đại có chế độ hoạt động này là tiêu chuẩn được gọi là bộ tự dao động.

Trong ngôn ngữ học

Khả năng của một ngôn ngữ để tạo ra các câu và cấu trúc lồng nhau. Ưu đãi cơ bản" con mèo đã ăn con chuột» có thể được mở rộng bằng đệ quy như Vanya đoán rằng con mèo đã ăn con chuột, thì như Katya biết rằng Vanya đoán rằng con mèo đã ăn con chuột và như thế. Đệ quy được coi là một trong những phổ quát ngôn ngữ, nghĩa là nó là đặc trưng của bất kỳ ngôn ngữ tự nhiên nào. Tuy nhiên, gần đây đã có cuộc thảo luận tích cực về khả năng không có đệ quy ở một trong những ngôn ngữ của Amazon - Pirahã, được nhà ngôn ngữ học Daniel Everett lưu ý ( Tiếng Anh) .

Trong văn hóa

Hầu hết các câu chuyện cười về đệ quy đều liên quan đến đệ quy vô hạn, trong đó không có điều kiện thoát, chẳng hạn như câu nói nổi tiếng: “để hiểu đệ quy, trước tiên bạn phải hiểu đệ quy”.

Một câu nói đùa rất phổ biến về đệ quy, gợi nhớ đến một mục trong từ điển:

Một số câu chuyện của Stanislaw Lem được dành cho các sự cố (có thể xảy ra) với đệ quy vô hạn:

  • câu chuyện về Ion the Quiet “Hành trình thứ mười bốn” từ “Nhật ký ngôi sao của Iyon the Quiet”, trong đó nhân vật chính chuyển tuần tự từ một bài báo về sepulki sang một bài báo về quá trình phân tách, từ đó đến một bài báo về sepulcaria, trong đó lại chứa tham chiếu đến bài viết “sepulki”:

Tôi tìm thấy thông tin ngắn gọn sau đây:
“SEPULKI là một yếu tố quan trọng của nền văn minh Ardrite (q.v.) từ hành tinh Enteropia (q.v.). Xem SEPULCARIA."
Tôi đã làm theo lời khuyên này và đọc:
“SEPULCARIA - thiết bị phân hủy (xem).”
Tôi đã tìm kiếm "Sepulenia"; nó đọc:
“SEPULATION là sự chiếm đóng của Ardrites (q.v.) từ hành tinh Enteropia (q.v.). Xem SEPULS.”

Lem S. “Nhật ký ngôi sao của Iyon the Quiet. Hành trình thứ mười bốn."

  • Một câu chuyện trong “Cyberiad” về một cỗ máy thông minh có đủ trí thông minh và sự lười biếng để tạo ra một cỗ máy tương tự nhằm giải quyết một vấn đề nhất định và giao phó giải pháp cho nó (kết quả là một sự đệ quy vô tận, khi mỗi cỗ máy mới tạo ra một cỗ máy tương tự và giao nhiệm vụ cho nó).
  • Các từ viết tắt đệ quy: GNU (GNU Not Unix), PHP (PHP: Hypertext Preprocessor), v.v.

Xem thêm

  • Trình tự trả về

Ghi chú


Quỹ Wikimedia. 2010.

  • Bộ nhớ video
  • Bức xạ điện từ

Xem “Đệ quy” là gì trong các từ điển khác:

    đệ quy- trả lại, lặp lại Từ điển các từ đồng nghĩa tiếng Nga. danh từ đệ quy, số từ đồng nghĩa: 1... Từ điển đồng nghĩa

    đệ quy- - [] đệ quy Theo nghĩa chung, việc tính toán một hàm bằng một thuật toán cụ thể. Ví dụ về các thuật toán như vậy là các công thức lặp lại rút ra phép tính của một thuật ngữ nhất định... ... Hướng dẫn dịch thuật kỹ thuật

    đệ quy- theo nghĩa chung, việc tính toán hàm bằng một thuật toán cụ thể. Ví dụ về các thuật toán như vậy là các công thức lặp lại, rút ​​ra cách tính một số hạng nhất định của một chuỗi (thường là số) từ việc tính toán một số ... Từ điển kinh tế và toán học

    đệ quy- Một mô hình trị liệu trong đó một số điều kiện hoặc tiêu chí được xây dựng trong tuyên bố ban đầu được lấy và áp dụng cho chính tuyên bố đó. Ví dụ: Tôi không có thời gian. Bạn đã phải mất bao nhiêu thời gian để đảm bảo rằng bạn... ... Bách khoa toàn thư tâm lý lớn

    ĐỆ LẠI- một phương pháp xác định hàm số, là đối tượng nghiên cứu của lý thuyết thuật toán và các ngành toán học khác. Hợp lý. Phương pháp này từ lâu đã được sử dụng trong số học để xác định các dãy số (tiến trình, số Fibonacci, v.v.).... ... Bách khoa toàn thư toán học

    đệ quy- (nền) (lat. recursio return). Một trong ba giai đoạn phát âm, thụt âm. Chuyển cơ quan phát âm sang trạng thái bình tĩnh hoặc bắt đầu phát âm âm thanh tiếp theo. Trong từ còn lại, đệ quy (thụt lề) khi phát âm [t] có thể chồng lên nhau ... ... Từ điển thuật ngữ ngôn ngữ T.V. Con ngựa con