Đệ quy - có phải là chia rẽ và chinh phục mã hay


11

Đệ quy - như tất cả chúng ta đều biết - là một trong những vấn đề đó - việc quấn lấy đầu bạn có cảm giác như đạt được một "cột mốc" trong hành trình lập trình của bạn.

Nhưng khi thực sự sử dụng nó trong các vấn đề trong thế giới thực - biết cơ chế đệ quy là chưa đủ - người ta cũng phải hiểu bản chất của các vấn đề trong đó đệ quy là giải pháp phù hợp nhất.

Vì vậy, câu hỏi của tôi là ...

  • "các mẫu vấn đề" yêu cầu giải pháp đệ quy là gì
  • là đệ quy một hình thức chiến lược "phân chia & chinh phục" hoặc một hình thức "tái sử dụng mã" - hoặc, là một mẫu thiết kế theo đúng nghĩa của nó
  • bạn có thể cho chúng tôi một ví dụ về một vấn đề trong thế giới thực, nơi đệ quy xuất hiện trong tâm trí như một giải pháp tức thời

- CẬP NHẬT -

rất nhiều câu trả lời đang đề cập đến "các vấn đề thực tế" khi đi ngang qua cây, của giai thừa, v.v. Tôi thích "các vấn đề thực tế THỰC SỰ" - hãy để tôi cho bạn một ví dụ ...

Chúng tôi đã có một đoạn văn bản LỚN (khoảng 30 MB văn bản dưới dạng danh sách được liên kết structs) và chúng tôi cần tạo một chỉ mục của nó để tìm kiếm toàn văn bản. Chúng tôi cần giữ toàn bộ chỉ mục trong bộ nhớ và lập chỉ mục lại văn bản cứ sau 10 phút.

Cứ sau 10 phút, chúng tôi sẽ so sánh toàn bộ văn bản (hai danh sách được liên kết, từng dòng) với một đoạn văn bản mới được tạo - để xem dòng nào đã được thay đổi - và sau đó chúng tôi sẽ chỉ lập lại dòng đó - theo cách đó chúng ta có thể tránh phải lập chỉ mục lại văn bản ENTIRE. Hãy nhớ rằng - chúng tôi cần tìm các điểm khác nhau giữa hai danh sách được liên kết 30 MB.

Một trong những đồng nghiệp của tôi đã đưa ra một chương trình tuyệt vời sử dụng đệ quy HEAVY để so sánh các dòng - và sau đó thu thập các vị trí mà các con gà khác nhau trong một mảng - vâng tôi biết nó nghe có vẻ khó hiểu - làm thế nào có thể đệ quy ở đây - nhưng nó đã làm.

Vấn đề là - làm thế nào anh ta có thể thấy rằng vấn đề này có thể được giải quyết một cách thông minh với việc sử dụng đệ quy nặng?


30 MB có thực sự lớn trong những ngày này khi mà hầu hết các máy tính có GB RAM và TB không gian ổ cứng?
JB King

30 MB có thể KHÔNG lớn - nhưng xem xét loại cấu trúc dữ liệu mà văn bản của chúng tôi đã được nhồi nhét vào - đó thực sự là một đoạn văn bản LỚN cho PROCESS - và DIFF.
treecoder

3
Không phải là người đi ngang qua một cấu trúc thư mục có thật THỰC SỰ không? Và tôi hoàn toàn không thấy, trong ví dụ của bạn, cách đệ quy nên không trực quan ở đây và tại sao việc sử dụng nó lại đặc biệt đáng chú ý. Đồng nghiệp của bạn đã thiết kế một thuật toán đệ quy, giống như bất kỳ thuật toán nào khác. Bạn cũng có thể hỏi làm thế nào Hoare có ý tưởng để giải quyết vấn đề sắp xếp theo cách đệ quy.
Konrad Rudolph

2
Tôi có đúng không khi nghĩ rằng bạn có nghĩa là "tái sử dụng mã" nhiều hơn là "thực hiện cùng một chuỗi các hoạt động với số lần không xác định"? Điều đó trái ngược với "tái sử dụng mã" theo nghĩa là viết mã chung để sử dụng ở nơi khác.
Andy Hunt

4
Nhưng truyền tải cây là một "vấn đề thực tế", mà nhiều người gặp phải hàng ngày.
Falcon

Câu trả lời:


16
  • "các mẫu vấn đề" yêu cầu giải pháp đệ quy là gì

Tôi sẽ không nói rằng có một thứ giống như một mô hình vấn đề cho việc sử dụng đệ quy. Mọi chức năng có thể được thực hiện với đệ quy cũng có thể được thực hiện lặp đi lặp lại, thường bằng cách đẩy và bật một ngăn xếp.

Đó là một vấn đề về biểu hiện và cũng là hiệu suất. Các thuật toán lặp thường có hiệu suất tốt hơn và dễ tối ưu hóa hơn. Tuy nhiên, các thuật toán đệ quy được hưởng lợi từ một biểu thức rõ ràng hơn và do đó thường dễ đọc, hiểu và thực hiện hơn.

Một số điều thậm chí không thể được thể hiện mà không có đệ quy, danh sách vô hạn chẳng hạn. Cái gọi là ngôn ngữ chức năng chủ yếu dựa vào đệ quy, vì đó là cách diễn đạt tự nhiên của chúng. Câu nói là: "Lập trình đệ quy là lập trình chức năng được thực hiện đúng".

  • là đệ quy một hình thức chiến lược "phân chia & chinh phục" hoặc một hình thức "tái sử dụng mã" - hoặc, là một mẫu thiết kế theo đúng nghĩa của nó

Tôi sẽ không gọi nó là một mẫu thiết kế. Đó là một vấn đề biểu hiện. Đôi khi một biểu thức đệ quy đơn giản là mạnh hơn và biểu cảm hơn và do đó dẫn đến mã tốt hơn và sạch hơn.

  • bạn có thể cho chúng tôi một ví dụ về một vấn đề trong thế giới thực, nơi đệ quy xuất hiện trong tâm trí như một giải pháp tức thời

Bất cứ điều gì cần đi qua cây sẽ được thể hiện đúng bằng thuật toán đệ quy.


7
"Mọi chức năng có thể được thực hiện với đệ quy cũng có thể được thực hiện lặp đi lặp lại, thường bằng cách đẩy và bật một ngăn xếp." Xét cho cùng, trong các ngôn ngữ sử dụng bộ nhớ dựa trên ngăn xếp, bạn đã đẩy và bật dữ liệu chức năng trên và ngoài ngăn xếp khi sử dụng đệ quy.
JAB

Chỉ khi bạn biên dịch hoặc giải thích ngôn ngữ bằng máy ;-) Ngoài ra, từ quan điểm rất cao, biểu thức và ngôn ngữ hoàn toàn độc lập với máy và phần cứng và HĐH, do đó, không nhất thiết phải là một ngăn xếp.
Falcon

À, vâng, bạn hoàn toàn đúng. Tôi nên nói "trong việc triển khai các trình biên dịch ngôn ngữ / ngôn ngữ sử dụng bộ nhớ dựa trên ngăn xếp".
JAB

Nói chung, bạn cũng đúng. Tôi không muốn xuất hiện nitpicking.
Falcon

2
Danh sách vô hạn có thể được thể hiện mà không cần đệ quy, ít nhất là không có triển khai đệ quy. Các trình tạo Python có thể làm điều đó, cũng như các trình tạo trong Biểu tượng mà Python dường như đã mượn ý tưởng từ đó. Tôi tin rằng F # có thể thực hiện thủ thuật này, mặc dù tôi không chắc chắn. Về cơ bản, máy phát điện là một trường hợp đặc biệt của các đồng quy (như đa nhiệm hợp tác) rất phù hợp để thực hiện các danh sách lười biếng. Mỗi khi một trình tạo "mang lại" một kết quả, người gọi sẽ lấy lại quyền kiểm soát và trình tạo sẽ ở trạng thái chờ cho đến khi kết quả tiếp theo được yêu cầu.
Steve314

8

là đệ quy một hình thức chiến lược "phân chia & chinh phục" hoặc một hình thức "tái sử dụng mã" - hoặc, là một mẫu thiết kế theo đúng nghĩa của nó

Cũng không. Phân chia & chinh phục sử dụng đệ quy. Nhưng đệ quy không nhất thiết phải phân chia & chinh phục vì điều sau có nghĩa là chia một vấn đề thành hai (hoặc nhiều hơn) và giải quyết từng phần một cách đối xứng. Trong đệ quy, bạn không làm điều này.

Tái sử dụng mã hoàn toàn không liên quan và một mẫu thiết kế xuất hiện ở cấp độ cao hơn nhiều. Chẳng hạn, ngay cả phân chia & chinh phục (cũng là một mẫu cấp cao hơn đệ quy) vẫn không được coi là một mẫu thiết kế - thay vào đó, nó là một mẫu thuật toán .

bạn có thể cho chúng tôi một ví dụ về một vấn đề trong thế giới thực, nơi đệ quy xuất hiện trong tâm trí như một giải pháp tức thời

Cây ngang. Hoặc nói chung là đồ thị đi qua. Điều này đáng chú ý bao gồm đi qua một cấu trúc thư mục.

Và tất nhiên, bất cứ điều gì sử dụng phân chia & chinh phục hoặc lập trình động vì cả hai đều được thể hiện một cách tự nhiên dưới dạng đệ quy.


Lập trình động không phải luôn luôn được thể hiện một cách tự nhiên như đệ quy. Trong thực tế, lập trình động đề cập nghiêm túc đến các phương pháp tiếp cận dạng bảng - không bao gồm ghi nhớ. Các ý kiến ​​dường như khác nhau về điều này, nhưng "lập trình" trong "lập trình động" thực sự là một thuật ngữ toán học, đề cập đến các phương pháp tiếp cận dạng bảng (một mẩu câu đố tôi nhặt được từ khóa học thuật toán mã nguồn mở MIT). Vì vậy, nghiêm ngặt, lập trình động khai thác cấu trúc tối ưu bằng cách sử dụng những gì thường được biểu diễn dễ dàng nhất như một vòng lặp đơn giản. Ghi nhớ có nhiều khả năng ngụ ý đệ quy, nhưng không nhất thiết.
Steve314

1
@ Steve314 Tôi đồng ý rằng việc triển khai thực tế DP (có thể là trong chương trình máy tính hoặc thủ công) hiếm khi sử dụng đệ quy. Nhưng ý tưởng vốn dĩ dựa trên mối quan hệ lặp lại - đó chỉ là một công thức đệ quy! - và một trường hợp cơ sở.
Konrad Rudolph

Tôi đồng ý rằng "cấu trúc tối ưu" (một giải pháp tối ưu có các giải pháp từng phần tối ưu) là một ý tưởng đệ quy. Đó là một quan điểm toán học / khoa học máy tính về đệ quy, không liên quan trực tiếp đến việc thực hiện - nhưng vai trò của đệ quy trong khoa học máy tính là một điểm quan trọng cần thực hiện. Rất ít thuật toán (và có lẽ không có mẫu thiết kế nào) là các công cụ quan trọng trong khoa học máy tính - hầu hết là các môn học thuần túy để nghiên cứu hơn là các công cụ được sử dụng để nghiên cứu một cái gì đó khác.
Steve314

4
what are the "problem patterns" that call for the solution of recursion

Xuất phát từ sự giống nhau của fractals, tôi sẽ nói rằng tự bình đẳng hoặc tự nhận dạng (hoặc được gọi là) là một mô hình vấn đề điển hình cho đệ quy. Tức là một vấn đề có thể được chia thành các vấn đề phụ có cấu trúc rất giống với vấn đề chính.

Trong giao diện cây được đề cập, mỗi cây phụ là một cây đầy đủ, giống như cây chính và cây chính có thể là cây phụ trong cây khác.

Vì vậy, tôi đoán rằng đồng nghiệp của bạn đã phát hiện ra các thuộc tính tự bình đẳng trong vấn đề lập chỉ mục của bạn. Hoặc anh ấy đã đi một con đường khác và biến vấn đề thành một đại diện tự bình đẳng.


1
+1 cho "một vấn đề có thể được chia thành các vấn đề phụ có cấu trúc rất giống với vấn đề chính"
treecoder

+1 và để diễn giải: nơi giải pháp vấn đề áp dụng cho các lớp con. Ví dụ trong thế giới thực của tôi là tìm kiếm các khoản phí thẻ tín dụng góp phần vào một "đợt". Phần mềm kế toán sẽ có các khoản phí riêng và tiền gửi hàng loạt vào tài khoản kiểm tra. Trường hợp của tôi có thể trở thành một câu hỏi ở đây vì stackoverflow không quá sắc nét về nó. stackoverflow.com/questions/14719806
Chris K

3

Vâng, đệ quy có thể được hiểu một cách dễ dàng nếu người ta sẽ cố gắng chuyển đổi các vòng lặp bắt buộc thành các chức năng chức năng. Dù sao, hãy cố gắng đưa ra câu trả lời cho tất cả các câu hỏi:

"các mẫu vấn đề" yêu cầu giải pháp đệ quy là gì

Nếu bạn có cấu trúc hoặc thuật toán giống như cây, bạn sẽ cần đệ quy. Nếu mã bắt buộc của bạn xử lý một ngăn xếp, bạn sẽ cần đệ quy. Nếu một bước nhất định trong thuật toán của bạn phụ thuộc vào các bước trước đó (nghĩ các vòng lặp), bạn cần đệ quy. Cần ở đây là để được giải thích là có thể sử dụng.

là đệ quy một hình thức chiến lược "phân chia & chinh phục" hoặc một hình thức "tái sử dụng mã" - hoặc, là một mẫu thiết kế theo đúng nghĩa của nó

Không ai. Phân chia và chinh phục sử dụng đệ quy nhưng có thể được thực hiện với các ngăn xếp. Tái sử dụng mã đề cập đến một cái gì đó khác. Các mẫu thiết kế phức tạp hơn đệ quy đơn giản.

bạn có thể cho chúng tôi một ví dụ về một vấn đề trong thế giới thực, nơi đệ quy xuất hiện trong tâm trí như một giải pháp tức thời

Phân tích cú pháp và mọi thứ liên quan đến cấu trúc cây. Ngay cả cấu trúc cây ngầm.


3

Nếu có một cách để đơn giản hóa nó để dễ dàng, đó có thể là đầu mối mà đệ quy có thể hoạt động. Bạn có thể thực hiện sắp xếp và tìm kiếm các ví dụ trong đó có các giải pháp đệ quy như Hợp nhất Sắp xếpTìm kiếm nhị phân tương ứng.

Một điều cần lưu ý là làm thế nào một số vấn đề có thể được giải quyết kém với đệ quy như một nhân tố.

Đối với một ví dụ trong thế giới thực mà tôi sử dụng đệ quy, việc tra cứu một cuốn sách từ kệ sách có thể được thực hiện khá dễ dàng theo cách đệ quy. Tôi chỉ nhìn vào cuốn sách và nếu đó không phải là điều tôi muốn, tôi chuyển sang cuốn tiếp theo. Tôi dừng lại khi tôi tìm thấy cuốn sách hoặc nhấn vào cuối hàng. Việc lặp lại kiểm tra một cuốn sách và chuyển sang cuốn tiếp theo có thể được thực hiện đệ quy. Có lẽ đây là quá thực tế của một ví dụ.


2

"các mẫu vấn đề" yêu cầu giải pháp đệ quy là gì

Nói một cách chung chung, đệ quy được gọi khi bạn giải quyết vấn đề trong đó f (x) = f (g (x)) . Trừ khi bạn ổn với đệ quy vô hạn, g (x) không nên đánh giá thành x .

là đệ quy một hình thức chiến lược "phân chia & chinh phục" hoặc một hình thức "tái sử dụng mã" - hoặc, là một mẫu thiết kế theo đúng nghĩa của nó

Không có cái nào ở trên. Đó chỉ là một cách để làm điều tương tự lặp đi lặp lại, đôi khi dựa trên các biến thể của đầu vào. Khái niệm này đã tồn tại lâu hơn nhiều so với các mẫu thiết kế, tái sử dụng mã hoặc thậm chí cả máy tính, cho vấn đề đó.

bạn có thể cho chúng tôi một ví dụ về một vấn đề trong thế giới thực, nơi đệ quy xuất hiện trong tâm trí như một giải pháp tức thời

Các yếu tố, chuỗi Fibonacci, truyền qua cây và rất nhiều vấn đề khác có thể được giải quyết bằng cách xem xét lại. Đệ quy theo nghĩa của một hàm gọi chính nó không nhất thiết là cách tốt nhất để thực hiện các loại điều đó; có nhiều cách khác để đạt được hiệu ứng tương tự (ví dụ: ngăn xếp và vòng lặp) có thể được mong muốn hơn.


-1

Khi bạn viết một thuật toán đệ quy, bạn thường dịch một định nghĩa đệ quy của vấn đề sang mã. Sau đó, bạn thậm chí không cần biết nó sẽ được thực thi như thế nào .

Đó là những gì xảy ra trong lập trình chức năng. Trong thực tế, bạn chỉ định những gì (định nghĩa) chứ không phải làm thế nào . Nói cách khác, bạn xác định cơ sở và sau đó xác định vấn đề của bạn trong thuật ngữ của một vấn đề phụ.

Ví dụ xem xét Factorialthuật toán

  • Xác định cơ sở: Yếu tố (1) = 1;
  • Xác định nhân tố n: Factorial (n) = n * Factorial (n-1);

Sau đó, khi bạn gặp phải một vấn đề, bạn nên suy nghĩ xem bạn có thể định nghĩa nó một cách đệ quy hay không, nếu bạn có thể định nghĩa nó một cách đệ quy, bạn gần như đã giải quyết nó.

Tuy nhiên, bất kỳ hàm đệ quy nào cũng không nên là một định nghĩa đệ quy. Bạn có thể định nghĩa cơ sở và liên quan (xác định) giải pháp của vấn đề chính với giải pháp (định nghĩa) của các vấn đề phụ. Nhưng đối với mối quan hệ này, bạn có thể cần một thủ tục.

Một ví dụ là MergeSorttrong đó mergecó thể là một thủ tục bắt buộc để liên kết định nghĩa hoặc giải pháp sắp xếp toàn bộ mảng với sắp xếp các mảng con.

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.