Phân tích độ phức tạp thuật toán về triển khai ngôn ngữ lập trình chức năng


10

Hôm nay tôi đã học được rằng phân tích thuật toán khác nhau dựa trên mô hình tính toán. Đó là điều mà tôi chưa bao giờ nghĩ tới hoặc nghe nói đến.

Một ví dụ được đưa ra cho tôi, được minh họa thêm bởi Người dùng @chi là:

Ví dụ: hãy xem xét nhiệm vụ: đã cho return . Trong RAM, điều này có thể được giải quyết trong vì truy cập mảng là thời gian không đổi. Sử dụng TM, chúng ta cần quét toàn bộ đầu vào, vì vậy đó làx i O ( 1 ) O ( n )(i,x1,,xn)xiO(1)O(n)

Điều này làm cho tôi tự hỏi về các ngôn ngữ chức năng; Theo hiểu biết của tôi, "Ngôn ngữ chức năng có liên quan mật thiết đến phép tính lambda" (từ một bình luận của Yuval Filmus ở đây ). Vì vậy, nếu các ngôn ngữ chức năng dựa trên tính toán lambda, nhưng chúng chạy trên các máy dựa trên RAM, thì cách phù hợp để thực hiện phân tích phức tạp trên các thuật toán được thực hiện bằng cách sử dụng các cấu trúc dữ liệu và ngôn ngữ thuần túy là gì?

Tôi chưa có cơ hội đọc Cấu trúc dữ liệu chức năng thuần túy nhưng tôi đã xem trang Wikipedia cho chủ đề này và dường như một số cấu trúc dữ liệu thay thế các mảng truyền thống bằng:

"Mảng có thể được thay thế bằng bản đồ hoặc danh sách truy cập ngẫu nhiên, thừa nhận thực hiện chức năng hoàn toàn, nhưng thời gian truy cập và cập nhật là logarit."

Trong trường hợp đó, mô hình tính toán sẽ khác, đúng không?


3
Tôi chắc chắn không phải là một chuyên gia về chủ đề này, nhưng tôi tin rằng tôi đã nghe nói rằng 1) một cỗ máy giống như (với mô hình chi phí riêng) có thể mô phỏng các chương trình RAM với yếu tố bổ sung (điều này có vẻ dễ chứng minh ) và 2) liệu yếu tố này có thực sự cần thiết hay không vẫn là một vấn đề mở. Hơn nữa, có thể lập luận rằng việc chỉ định chi phí O (1) cho truy cập mảng trong mô hình RAM là quá hào phóng. Trong phần cứng, truy cập bộ nhớ phải đi qua trong đó là bộ nhớ vật lý kích thước. O ( log n ) nO(logn)O(logn)n
chi

1
Ngoài ra, hãy nhớ rằng hầu như tất cả các ngôn ngữ FP trong thế giới thực đều có mảng ở một số dạng, với thời gian truy cập được đảm bảo (như trong các ngôn ngữ bắt buộc). Điều này thường được giải quyết thêm chúng như là một ngôn ngữ nguyên thủy. O(1)
chi

1
Một ví dụ về một mô hình tính toán khác sẽ là số lần giảm beta được thực hiện trên thuật ngữ tính toán lambda. Trong FP, chúng tôi sử dụng mô hình ram được trang điểm như một phép tính lambda, nếu điều đó hợp lý
Kurt Mueller

1
@KurtMueller Lưu ý rằng chúng ta có thể có một thuật ngữ lambda có kích thước chỉ sau khi giảm bete. Điều này làm cho mô hình chi phí đếm số lượng beta không thực tế. Một điều tốt hơn có thể được cho là có thể cân nhắc từng bước theo kích thước của các điều khoản trong tay. Tuy nhiên, đây không phải là mô hình duy nhất có thể: đánh giá tối ưu các thuật ngữ lambda không áp dụng beta theo cách ngây thơ, thích một số máy giảm đồ thị tinh vi hơn. Trong trường hợp như vậy, đếm betas có lẽ sẽ không phù hợp. O ( n )O(2n)O(n)
chi

1
Lưu ý rằng bạn cũng cần biết liệu ngôn ngữ chức năng của bạn là háo hức hay lười biếng / nghiêm khắc hay không nghiêm ngặt. Gần đây tôi đã gặp một tình huống trong đó một thuật toán trong thế giới thực là đa thức trong Haskell (không nghiêm ngặt) nhưng bản dịch ngây thơ sang OCaml (nghiêm ngặt) là theo cấp số nhân.
Eric Lippert

Câu trả lời:


6

Nó phụ thuộc vào ngữ nghĩa của ngôn ngữ chức năng của bạn. Bạn không thể phân tích thuật toán trên các ngôn ngữ lập trình một cách cô lập, vì bạn không biết ý nghĩa thực sự của câu lệnh là gì. Các đặc điểm kỹ thuật cho ngôn ngữ của bạn cần cung cấp đủ ngữ nghĩa chi tiết. Nếu ngôn ngữ của bạn chỉ định mọi thứ theo tính toán lambda, bạn cần một số biện pháp giảm chi phí (chúng có phải là O (1) hay chúng phụ thuộc vào quy mô của thuật ngữ bạn giảm?).

Tôi nghĩ rằng hầu hết các ngôn ngữ chức năng không thực hiện theo cách đó và thay vào đó cung cấp các câu lệnh hữu ích hơn như "các lệnh gọi hàm là O (1), nối vào đầu danh sách là O (1)", đại loại như vậy.


Tôi tin rằng tôi hiểu được câu trả lời của bạn (sự hiểu lầm rất có thể là do tôi thiếu hiểu biết về tính toán lambda): Về cơ bản, bạn đang nói rằng bạn phải phân tích từng trường hợp cụ thể (trường hợp là ngôn ngữ), chứ không phải là một cách chung, bởi vì các hoạt động nhất định có ý nghĩa khác nhau cho mỗi ngôn ngữ. Tôi hiểu có đúng không?
Abdul

Đúng. Nhà thiết kế ngôn ngữ của bạn cần cho bạn biết những gì bạn có thể viết bằng ngôn ngữ thực sự có nghĩa là gì trước khi bạn có thể phân tích thời gian chạy của thuật toán.
adrianN

"Bạn không thể phân tích thuật toán trên các ngôn ngữ lập trình một cách tách biệt" - điều này có đề cập đến các ngôn ngữ hoặc ngôn ngữ FP nói chung không? Nếu nó được đề cập trước đó, thì làm thế nào để chúng ta phân tích thuật toán trong trường học theo cách chung như vậy, ví dụ như phân tích trên các vấn đề Java, C / C ++, Python? Có phải vì tất cả đều rất giống nhau? Hoặc là bởi vì các cấu trúc dữ liệu & ADT cơ bản đều giống nhau và được thực hiện theo cách tương tự? Hoặc cuối cùng, chẳng qua là vì các khóa học này chỉ đơn giản là vì lợi ích của giáo dục, và không nhất thiết cần phải nghiêm chỉnh chính xác?
Abdul

1
Điều đó đúng với tất cả các ngôn ngữ lập trình. Để chính xác, trước tiên bạn cần sửa một kiểu máy, nói RAM và (một số ít) các hướng dẫn mà nó hỗ trợ. Bạn chỉ có thể thực hiện phân tích trên các chương trình chỉ sử dụng các hướng dẫn đó. Sau đó, bạn có thể nghĩ về việc ánh xạ ngôn ngữ lập trình của bạn sang mô hình máy đó. Sau đó, bạn có thể phân tích các chương trình bằng ngôn ngữ lập trình. Đối với một điều trị rất nghiêm ngặt kiểm tra cách Knuth làm điều đó trong Nghệ thuật lập trình máy tính. Rất nhiều điều này có thể được đơn giản hóa vì các hằng số ẩn O lớn.
adrianN
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.