Thuật toán phân chia và chinh phục - Tại sao không chia thành nhiều phần hơn hai?


33

Trong các thuật toán phân chia và chinh phục như quicksort và mergesort, đầu vào thường (ít nhất là trong các văn bản giới thiệu) được chia làm hai và hai tập dữ liệu nhỏ hơn sau đó được xử lý đệ quy. Điều này có ý nghĩa với tôi rằng điều này giúp giải quyết vấn đề nhanh hơn nếu hai nửa mất ít hơn một nửa công việc xử lý toàn bộ tập dữ liệu. Nhưng tại sao không chia bộ dữ liệu thành ba phần? Bốn? n ?

Tôi đoán công việc phân chia dữ liệu thành nhiều, nhiều bộ phụ làm cho nó không có giá trị, nhưng tôi thiếu trực giác để thấy rằng một người nên dừng lại ở hai bộ phụ.

Tôi cũng đã thấy nhiều tài liệu tham khảo về quicksort 3 chiều. Khi nào thì nhanh hơn? Những gì được sử dụng trong thực tế?


Hãy thử tạo một thuật toán tương tự như quicksort chia một mảng thành ba phần.
gnasher729

Câu trả lời:


49

Điều này có ý nghĩa với tôi rằng điều này giúp giải quyết vấn đề nhanh hơn nếu hai nửa mất ít hơn một nửa công việc xử lý toàn bộ tập dữ liệu.

Đó không phải là bản chất của thuật toán phân chia và chinh phục. Thông thường, vấn đề là các thuật toán không thể "đối phó với toàn bộ tập dữ liệu". Thay vào đó, nó được chia thành các phần nhỏ để giải quyết (như sắp xếp hai số), sau đó chúng được giải một cách tầm thường và kết quả được kết hợp lại theo cách mang lại giải pháp cho tập dữ liệu đầy đủ.

Nhưng tại sao không chia bộ dữ liệu thành ba phần? Bốn? n?

Chủ yếu là vì tách nó thành nhiều hơn hai phần và kết hợp lại nhiều hơn hai kết quả dẫn đến việc thực hiện phức tạp hơn nhưng không thay đổi đặc tính cơ bản (Big O) của thuật toán - sự khác biệt là một yếu tố không đổi và có thể dẫn đến chậm lại nếu sự phân chia và kết hợp lại của hơn 2 tập con sẽ tạo thêm chi phí.

Ví dụ: nếu bạn thực hiện sắp xếp hợp nhất 3 chiều, thì trong giai đoạn kết hợp lại, bây giờ bạn phải tìm 3 phần tử lớn nhất cho mỗi phần tử, yêu cầu 2 so sánh thay vì 1, vì vậy bạn sẽ thực hiện gấp đôi tổng số so sánh . Đổi lại, bạn giảm độ sâu đệ quy theo hệ số ln (2) / ln (3) == 0,63, do đó bạn có số lần hoán đổi ít hơn 37%, nhưng so sánh nhiều hơn 2 * 0,63 == 26% (và truy cập bộ nhớ). Cho dù đó là tốt hay xấu phụ thuộc vào cái nào đắt hơn trong phần cứng của bạn.

Tôi cũng đã thấy nhiều tài liệu tham khảo về quicksort 3 chiều. Khi nào thì nhanh hơn?

Rõ ràng, một biến thể trục kép của quicksort có thể được chứng minh là yêu cầu cùng số lượng so sánh nhưng trung bình ít hơn 20% giao dịch hoán đổi, vì vậy đó là một lợi ích ròng.

Những gì được sử dụng trong thực tế?

Ngày nay hầu như không ai lập trình các thuật toán sắp xếp của riêng họ nữa; họ sử dụng một cái được cung cấp bởi một thư viện. Ví dụ: API Java 7 thực sự sử dụng quicksort trục kép.

Những người thực sự lập trình thuật toán sắp xếp của riêng họ vì một số lý do sẽ có xu hướng dính vào biến thể 2 chiều đơn giản vì ít có khả năng xảy ra lỗi hơn hiệu suất tốt hơn 20% trong hầu hết thời gian. Hãy nhớ rằng: cho đến nay, cải tiến hiệu suất quan trọng nhất là khi mã chuyển từ "không hoạt động" sang "hoạt động".


1
Lưu ý nhỏ: Java 7 sử dụng Dual-Pivot quicksort chỉ khi sắp xếp nguyên thủy. Để sắp xếp các đối tượng, nó sử dụng Timsort.
Bakuriu

1
+1 cho "Ngày nay hầu như không ai lập trình các thuật toán sắp xếp của riêng họ nữa" và (quan trọng hơn) "Hãy nhớ rằng: cho đến nay, cải tiến hiệu suất quan trọng nhất là khi mã chuyển từ" không hoạt động "sang" hoạt động "." Tuy nhiên, tôi muốn biết liệu chi phí đó có còn tầm thường hay không, ví dụ, người ta chia bộ dữ liệu thành nhiều, nhiều phần. Vì nó như vậy xảy ra, vì vậy có những người khác: bealto.com/gpu-sorting_intro.html stackoverflow.com/questions/1415679/... devgurus.amd.com/thread/157159
AndrewJacksonZA

Tôi hơi chậm. Bất cứ ai có thể giải thích tại sao phải mất hơn 2 * 0,69 so sánh? Không chắc chắn 0,69 đến từ đâu.
jeebface

@jeebface oops, đó là một lỗi đánh máy (hiện đã được sửa). Đó là 0,63 (giảm độ sâu đệ quy), sau đó kết quả của 26% nữa cũng có kết quả.
Michael Borgwardt

30

Nói một cách không có triệu chứng, nó không thành vấn đề. Ví dụ: tìm kiếm nhị phân tạo ra các  so sánh log 2 n và so sánh tìm kiếm ternary tạo ra các  so sánh log 3 n. Nếu bạn biết logarit của mình, bạn biết rằng log a  x = log b  x / log b  a, vì vậy tìm kiếm nhị phân chỉ thực hiện khoảng 1 / log 3 2 ≈ 1,5 lần so sánh so với tìm kiếm ternary. Đây cũng là lý do không ai từng chỉ định cơ sở của logarit trong ký hiệu Oh lớn: Nó luôn là một yếu tố không đổi khỏi logarit trong một cơ sở nhất định, bất kể thực tế cơ sở là gì. Vì vậy, việc phân tách vấn đề thành nhiều tập con hơn không cải thiện độ phức tạp thời gian và thực tế không đủ để vượt xa logic phức tạp hơn. Trong thực tế, sự phức tạp đó có thể ảnh hưởng tiêu cực đến hiệu suất thực tế, làm tăng áp lực bộ đệm hoặc làm cho tối ưu hóa vi mô trở nên ít khả thi hơn.

Mặt khác, một số cấu trúc dữ liệu cây-ish sử dụng hệ số phân nhánh cao (lớn hơn nhiều so với 3, thường là 32 hoặc hơn), mặc dù thường vì các lý do khác. Nó cải thiện việc sử dụng hệ thống phân cấp bộ nhớ: các cấu trúc dữ liệu được lưu trữ trong RAM giúp sử dụng bộ nhớ cache tốt hơn, các cấu trúc dữ liệu được lưu trữ trên đĩa yêu cầu ít đọc hơn HDD-> RAM.


Có, tìm kiếm octree cho một ứng dụng cụ thể của cấu trúc cây nhị phân nhiều hơn.
daaxix

@daaxix btree có lẽ phổ biến hơn.
Jules

4

Có các thuật toán tìm kiếm / sắp xếp chia nhỏ không phải bởi hai, mà bởi N.

Một ví dụ đơn giản là tìm kiếm bằng mã hóa băm, mất thời gian O (1).

Nếu hàm băm là bảo toàn thứ tự, nó có thể được sử dụng để tạo thuật toán sắp xếp O (N). (Bạn có thể nghĩ về bất kỳ thuật toán sắp xếp nào giống như thực hiện N tìm kiếm nơi một số sẽ đi đến kết quả.)

Vấn đề cơ bản là, khi một chương trình kiểm tra một số dữ liệu và sau đó nhập vào một số trạng thái sau, có bao nhiêu trạng thái sau đây và mức độ gần bằng nhau là xác suất của chúng?

Khi một máy tính thực hiện so sánh hai số, sau đó nhảy hoặc không, nếu cả hai đường dẫn đều có khả năng như nhau, bộ đếm chương trình "biết" thêm một chút thông tin trên mỗi đường dẫn, vì vậy trung bình nó đã "học" được một bit Nếu một vấn đề yêu cầu các bit M được học, thì sử dụng các quyết định nhị phân, nó không thể nhận được câu trả lời trong ít hơn các quyết định M. Vì vậy, ví dụ, việc tìm kiếm một số trong bảng được sắp xếp có kích thước 1024 không thể được thực hiện trong ít hơn 10 quyết định nhị phân đó, nếu chỉ vì ít hơn sẽ không có đủ kết quả, nhưng chắc chắn có thể được thực hiện nhiều hơn thế.

Khi một máy tính lấy một số và biến nó thành một chỉ mục thành một mảng, nó sẽ "học" để ghi lại cơ sở 2 của số lượng phần tử trong mảng và nó thực hiện nó trong thời gian không đổi. Ví dụ: nếu có một bảng nhảy gồm 1024 mục, tất cả đều có khả năng ít nhiều bằng nhau, sau đó nhảy qua bảng đó "học" 10 bit. Đó là mẹo cơ bản đằng sau mã hóa băm. Một ví dụ sắp xếp của việc này là cách bạn có thể sắp xếp một cỗ bài. Có 52 thùng, mỗi thùng một thẻ. Ném từng thẻ vào thùng của nó, và sau đó múc tất cả chúng lên. Không yêu cầu chia nhỏ.


1

Vì đây là một câu hỏi về sự phân chia và chinh phục chung, không chỉ là sắp xếp, tôi ngạc nhiên không ai đưa ra Định lý tổng thể

Tóm lại, thời gian hoạt động của các thuật toán phân chia và chinh phục được xác định bởi hai lực đối kháng: lợi ích bạn có được từ việc biến các vấn đề lớn hơn thành các vấn đề nhỏ và cái giá bạn phải trả khi phải giải quyết nhiều vấn đề hơn. Tùy thuộc vào các chi tiết của thuật toán, nó có thể hoặc không thể trả tiền để chia một vấn đề thành nhiều hơn hai phần. Nếu bạn chia thành cùng một số bài toán con ở mỗi bước và bạn biết độ phức tạp thời gian của việc kết hợp các kết quả ở mỗi bước, Định lý tổng thể sẽ cho bạn biết độ phức tạp thời gian của thuật toán tổng thể.

Các Karatsuba thuật toán cho phép nhân sử dụng một phân chia 3 chiều và chinh phục để đạt được một thời gian chạy của O (3 n ^ log_2 3) mà nhịp đập O (n ^ 2) vì thuật toán nhân thông thường (n là số chữ số trong số).


Trong định lý Master, số lượng các vấn đề phụ bạn tạo ra không phải là yếu tố duy nhất. Trong Karatsuba và anh em họ Strassen, sự cải tiến thực sự đến từ các giải pháp hợp nhất thông minh của một số vấn đề phụ, do đó bạn giảm số lượng các cuộc gọi đệ quy về các vấn đề phụ. Nói tóm lại,b định lý tổng thể đi lên đòi hỏi bạn phải ađi lên chậm hơn để bạn có sự cải thiện trong việc phân chia hơn nữa.
Được thông báo vào

-4

Do tính chất nhị phân của nó, một máy tính rất hiệu quả trong việc chia mọi thứ thành 2 và không quá nhiều trong 3. Bạn có được một phép chia 3 bằng cách chia 2 trước rồi chia một trong các phần lại thành 2. Vì vậy, nếu bạn cần chia bằng 2 để có được 3 phần của bạn, bạn cũng có thể chia thành 2.

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.