Các cân nhắc để xác định xem bạn có thể sử dụng đệ quy để giải quyết vấn đề không?


10

Đôi khi trong các cuộc phỏng vấn, tôi có thể sử dụng đệ quy để giải quyết vấn đề (chẳng hạn như thêm 1vào một số nguyên chính xác vô hạn) hoặc khi vấn đề xuất hiện phù hợp để sử dụng đệ quy. Đôi khi, có thể chỉ là do sử dụng đệ quy rất nhiều để giải quyết vấn đề, do đó, không cần suy nghĩ nhiều, đệ quy được sử dụng để giải quyết vấn đề.

Tuy nhiên, những cân nhắc trước khi bạn có thể quyết định nó là phù hợp để sử dụng đệ quy để giải quyết vấn đề?


Một số suy nghĩ tôi đã có:

Nếu chúng ta sử dụng đệ quy trên dữ liệu bị giảm một nửa mỗi lần, có vẻ như không có vấn đề gì khi sử dụng đệ quy, vì tất cả dữ liệu có thể vừa với 16GB RAM, hoặc thậm chí là ổ cứng 8TB, có thể được xử lý bằng cách đệ quy chỉ sâu 42 cấp. (vì vậy không có tràn ngăn xếp (Tôi nghĩ trong một số môi trường, ngăn xếp có thể sâu 4000 cấp, hơn 42, nhưng đồng thời, nó cũng phụ thuộc vào số lượng biến cục bộ của bạn, vì mỗi ngăn xếp cuộc gọi, chiếm nhiều bộ nhớ hơn nếu có nhiều biến cục bộ và kích thước bộ nhớ, không phải mức, sẽ xác định tràn ngăn xếp)).

Nếu bạn tính toán các số Fibonacci bằng cách sử dụng đệ quy thuần túy, bạn thực sự phải lo lắng về độ phức tạp của thời gian, trừ khi bạn lưu trữ các kết quả trung gian.

Và làm thế nào về việc thêm 1vào một số nguyên chính xác vô hạn? Có thể điều đó gây tranh cãi, vì, bạn sẽ làm việc với các số có 3000 chữ số dài hay 4000 chữ số, lớn đến mức có thể gây ra lỗi tràn ngăn xếp? Tôi không nghĩ về nó, nhưng có lẽ câu trả lời là không, chúng ta không nên sử dụng đệ quy, mà chỉ sử dụng một vòng lặp đơn giản, bởi vì nếu trong một số ứng dụng, con số thực sự cần phải dài 4000 chữ số, để kiểm tra một số các thuộc tính của số, chẳng hạn như số đó có phải là số nguyên tố hay không.

Câu hỏi cuối cùng là: những cân nhắc trước khi bạn có thể quyết định sử dụng đệ quy để giải quyết vấn đề là gì?


7
Nó thực sự khá đơn giản: "Giải pháp có tầm thường không nếu tôi có thể cho rằng giải pháp cho một vấn đề nhỏ hơn một chút được biết đến?"
Kilian Foth

nhưng những gì về số Fibonacci hoặc thêm 1vào số nguyên chính xác vô hạn? Bạn có thể nói, vâng, chúng giảm xuống một vấn đề nhỏ hơn, nhưng đệ quy thuần túy không phù hợp với nó
phân cực

Bạn có thể thấy điều này hữu ích - stackoverflow.com/questions/3021/
Kẻ

Câu trả lời:


15

Một xem xét là liệu thuật toán của bạn được dự định là một giải pháp trừu tượng hay một giải pháp thực thi thực tế. Trong trường hợp trước, các thuộc tính bạn đang tìm kiếm là tính chính xác và dễ hiểu cho đối tượng mục tiêu của bạn 1 . Trong trường hợp sau, hiệu suất cũng là một vấn đề. Những cân nhắc này có thể ảnh hưởng đến sự lựa chọn của bạn.

Một xem xét thứ hai (đối với một giải pháp thực tế) là liệu ngôn ngữ lập trình (hay nghiêm ngặt hơn là việc thực hiện nó) mà bạn đang sử dụng có thực hiện loại bỏ cuộc gọi đuôi không? Nếu không có loại bỏ cuộc gọi đuôi, đệ quy chậm hơn lặp lại và đệ quy sâu có thể dẫn đến các vấn đề tràn chồng.

Lưu ý rằng một giải pháp đệ quy (chính xác) có thể được chuyển đổi thành một giải pháp không đệ quy tương đương, do đó bạn không nhất thiết phải đưa ra lựa chọn khó khăn giữa hai phương pháp.

Cuối cùng, đôi khi sự lựa chọn giữa các công thức đệ quy và không đệ quy được thúc đẩy bởi nhu cầu chứng minh (theo nghĩa chính thức) về một thuật toán. Công thức đệ quy trực tiếp hơn cho phép bằng chứng cảm ứng.


1 - Điều này bao gồm các cân nhắc như liệu đối tượng mục tiêu ... và điều này có thể bao gồm các lập trình viên đọc mã thực tế ... sẽ xem một kiểu giải pháp là "tự nhiên" hơn các kiểu khác. Khái niệm "tự nhiên" sẽ thay đổi tùy theo từng người, tùy thuộc vào cách họ học lập trình hoặc thuật toán. (Tôi thách thức bất cứ ai đề xuất "tính tự nhiên" là tiêu chí chính để quyết định sử dụng đệ quy (hoặc không) để định nghĩa "tính tự nhiên" theo thuật ngữ khách quan; tức là bạn sẽ đo lường nó như thế nào.)


2
Một số vấn đề chỉ đơn giản là thể hiện tự nhiên hơn bằng cách sử dụng đệ quy. Cây ngang, chẳng hạn.
Frank Hileman

Đã cập nhật câu trả lời của tôi để giải quyết vấn đề đó,
Stephen C

1
Liên quan đến "tính tự nhiên": ví dụ, cây đi qua mà không có đệ quy, có xu hướng tạo ra mã mục đích lớn hơn, ít chung hơn. Ví dụ, xem xét sử dụng các cuộc gọi đa hình để đi qua cây, với hành vi khác nhau cho các nút lá và hỗn hợp. Điều này là không thể nếu không có đệ quy.
Frank Hileman

1) Bạn đã chấp nhận thử thách của tôi để xác định "tự nhiên" chưa? 2) Vì có thể mô phỏng đệ quy bằng cấu trúc dữ liệu ngăn xếp, nên cũng có thể thực hiện chuyển đổi cây theo cách đó. Nó có thể không phải là cách hiệu quả nhất ... và nó sẽ không cung cấp cho bạn mã dễ đọc nhất ... nhưng chắc chắn là có thể, và thực tế để làm điều đó.
Stephen C

Nhân tiện, ngôn ngữ lập trình đầu tiên mà tôi học được (FORTRAN 4) hoàn toàn không hỗ trợ đệ quy.
Stephen C

1

Là một lập trình viên C / C ++, sự cân nhắc hàng đầu của tôi là hiệu năng. Quá trình quyết định của tôi là một cái gì đó như:

  1. Độ sâu tối đa của ngăn xếp cuộc gọi là gì? Nếu quá sâu, hãy thoát khỏi đệ quy. Nếu nông, đi đến 2.

  2. Chức năng này có khả năng là nút cổ chai trong chương trình của tôi không? Nếu có, chuyển đến 3. Nếu không, hãy giữ đệ quy. Nếu không chắc chắn, hãy chạy một hồ sơ.

  3. Phần nhỏ thời gian CPU dành cho các cuộc gọi chức năng đệ quy là gì? Nếu các lệnh gọi hàm mất ít thời gian hơn đáng kể so với phần còn lại của thân hàm, bạn có thể sử dụng đệ quy.


0

Tuy nhiên, những cân nhắc trước khi bạn có thể quyết định nó là phù hợp để sử dụng đệ quy để giải quyết vấn đề?

Khi viết các hàm trong Scheme, tôi thấy tự nhiên khi viết các hàm đệ quy đuôi mà không cần suy nghĩ quá nhiều.

Khi viết các hàm trong C ++, tôi thấy mình đang tranh luận trước khi sử dụng hàm đệ quy. Các câu hỏi mà tôi tự hỏi mình là:

  • Tính toán có thể được thực hiện bằng thuật toán lặp không? Nếu có, sử dụng một cách tiếp cận lặp đi lặp lại.

  • Độ sâu của đệ quy có thể tăng theo kích thước của mô hình không? Gần đây tôi đã gặp phải một trường hợp độ sâu của đệ quy tăng lên gần 13000 do kích thước của mô hình. Tôi đã phải chuyển đổi hàm để sử dụng một thuật toán lặp lại vội vàng.

    Vì lý do này, tôi không khuyên bạn nên viết thuật toán duyệt cây bằng các hàm đệ quy. Bạn không bao giờ biết khi nào cây trở nên quá sâu cho môi trường thời gian chạy của bạn.

  • Hàm có thể trở nên quá phức tạp bằng cách sử dụng thuật toán lặp không? Nếu có, sử dụng hàm đệ quy. Tôi đã không thử viết qsortbằng cách sử dụng một cách tiếp cận lặp đi lặp lại nhưng tôi có cảm giác sử dụng một hàm đệ quy là tự nhiên hơn cho nó.


0

Đối với các số Fibonacci, "đệ quy" ngây thơ là hoàn toàn ngu ngốc. Đó là bởi vì nó dẫn đến cùng một bài toán con được giải quyết nhiều lần.

Thực tế, có một biến thể tầm thường của các số Fibonacci trong đó phép đệ quy rất hiệu quả: Cho một số n ≥ 1, tính cả số nguyên (n) và số sợi (n-1). Vì vậy, bạn cần một hàm trả về hai kết quả, hãy gọi hàm này là fib2.

Việc thực hiện khá đơn giản:

function fib2 (n) -> (fibn, fibnm1) {
    if n ≤ 1 { return (1, 1) }
    let (fibn, fibnm1) = fib2 (n-1)
    return (fibn + fibnm1, fibn)
}

Bạn có nghĩ rằng bạn có thể viết chương trình bằng một ngôn ngữ chung? và bạn fib2trả về một cặp số và bạn fib2()không khớp với giao diện của fib(), được cho một số, trả về một số. Có vẻ như bạn fib(n)sẽ quay trở lại fib2(n)[0]nhưng xin hãy cụ thể
phân cực
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.