2016-05-30 19 views
5

Các tài liệu nói Data.Sequence.reverse là O (n). Không thể một loại chuỗi hữu hạn được "đảo ngược" bằng cách đặt cờ? (Có hiệu quả, mà dấu chấm hết cho "bắt đầu đếm" từ.)tại sao Data.Sequence.reverse O (n)?

+3

Có thể, nhưng sau đó bạn phải mở rộng cấu trúc để mang lá cờ đó xung quanh. Rõ ràng các nhà bảo trì của Data.Sequence quyết định rằng nó không đáng giá. – Cubic

+1

Tôi sẽ viết một biến thể mang cờ tùy chỉnh 'Seq', nếu tôi cần nó. – chi

+0

Đối với hầu hết các trường hợp sử dụng, người dùng sẽ không hài lòng về chi phí hoạt động mà việc quản lý thêm một lá cờ sẽ được giới thiệu. Hãy để 'Seq' là những gì nó được. Những gì bạn cần là một wrapper đơn giản trên nó, mà sẽ trừu tượng trên lá cờ là tốt. Chúng ta có hai sự trừu tượng rõ ràng. –

Trả lời

3

Nếu bạn chỉ có một cờ, bạn sẽ gặp sự cố. Giả sử tôi muốn

xs <> reverse xs 

Tôi có thể tính toán điều đó như thế nào? Chỉ với một lá cờ, tôi phải thực hiện một sự đảo ngược đắt tiền trước khi phụ thêm. Để thực sự thực hiện điều này ngay, bạn cần thêm cờ, trải rộng trên cây. Tất cả xung quanh cây. Mỗi nút trong cây cần mang thông tin đảo ngược. Điều này là chắc chắn có thể, nhưng nó hơi đắt tiền. Thậm chí tệ hơn, mọi bước của mọi hoạt động đều phải quản lý các cờ đảo ngược một cách thích hợp, đó là một chút ác mộng đối với bất cứ ai phải viết và duy trì mã. Ý tưởng này đã được thực hiện trong một bài báo ở đâu đó (tôi không nhớ cái nào) nhưng nó không có niềm vui trong thực tế.

Nếu bạn có thuật toán dựa vào việc đảo ngược, bạn nên sử dụng loại trình tự tùy chỉnh có nhiều cờ và chỉ bao gồm các thao tác bạn cần (nếu bạn đặt loại của mình trên Data.Sequence, có lẽ bạn nên ăn cắp một chút từ mỗi trường kích thước). Nhưng tôi không nghĩ rằng các thuật toán như vậy là rất phổ biến.

+0

Ý của bạn là '><'? Nếu vậy, tôi không chắc tại sao nhu cầu phải chịu chi phí đảo ngược khi nối là phản đối một lá cờ duy nhất. Điều này chỉ làm chậm chi phí cho đến khi nó thực sự cần thiết, tôi nghĩ vậy? – Alan

+0

@Alan, '<>' là (tùy thuộc vào phiên bản thư viện cơ sở) hoặc là một hàm của kiểu '(<>) :: Monoid a => a -> a -> a' hoặc phương thức của lớp' Semigroup' với một chữ ký tương tự. Nó được biết đến rộng rãi hơn '><' trong cộng đồng Haskell nói chung, và cho các chuỗi '(<>) = (><)'. Trì hoãn các chi phí cho đến khi nó cần thiết có thể là tốt, nhưng cũng có thể làm cho hiệu suất khó dự đoán và trong nhiều trường hợp xấu. Trong thực tế, tôi đã làm việc để làm cho một số hoạt động chuỗi làm công việc của họ * nhiều hơn * hăm hở để chống lại điều này, và đã quản lý để cải thiện hiệu suất điểm chuẩn khá nhiều .. – dfeuer

1

Vâng, bạn có thể định nghĩa

data S a = S { forward :: Seq a , backward :: Seq a } 

và sau đó thực hiện tất cả các hoạt động với bất biến mà

forall a :: Type, s :: S a : reverse (forward s) == backward s . 

này tăng gấp đôi chi phí của mỗi nhưng cung cấp thời gian không đổi reverse.

4

Có, có thể.

Tuy nhiên, điều này cho biết thêm một mức giá nhỏ cho mỗi lần tra cứu (trước hết phải kiểm tra cờ!) Và khoản hoàn trả chỉ đến với người dùng sử dụng reverse rất nhiều. Nhóm thứ hai này có lẽ được đánh giá là nhỏ. Vì việc ném cờ thích hợp lên loại có thể được thực hiện phía máy khách, nên có nghĩa là không phải là để trả giá này trong thư viện cho mỗi lần sử dụng mọi chức năng. Thay vào đó, lập trình viên có ý định gọi reverse rất nhiều buộc phải thực hiện lựa chọn cân bằng hiệu suất này nếu họ muốn, điều đó có vẻ hoàn toàn hợp lý với tôi.

+1

"người đầu tiên phải kiểm tra cờ!" không cho mỗi phần tử (chỉ một lần cho mỗi cuộc gọi hàm), chỉ hoạt động '><' yêu cầu xây dựng lại toàn bộ cấu trúc – josejuan

0

Tôi nghĩ rằng có thể được thực hiện trong O(1) nếu kết nối >< chấp nhận nối xs >< reversed ys mà không bị phạt (một số yêu cầu hack vào cấu trúc cây ngón tay).

Nhìn để Data.Sequence mọi hoạt động có một đối tác ngược lại (ví dụ takeWhileL ~ takeWhileR.) Hoặc mất O(n) (hoặc tồi tệ nhất;. Ví dụ zip mất O(min(n1,n2)) sau đó, đảo ngược seq1 khi n1<n2 và như vậy) trừ >< hoạt động.

Trong mọi trường hợp, xs >< ys sẽ mất O(min(|xs|,|ys|)) là trường hợp xấu nhất (hai trong bốn trường hợp có thể). Đây là trong thực tế tốt hơn một chút so với O(n) (bình thường hóa theo một hướng duy nhất và đảo ngược bao giờ là cần thiết).

Sau đó, câu hỏi là:

có thể được thực hiện tốt hơn so với xs >< reversed ysO(min(|xs|,|ys|))?

(lý tưởng O(log(min(|xs|,|ys|))))

Tất nhiên, nếu bạn không sử dụng >< hoạt động, hoạt động ngược lại mất O(1) (ví dụ. Sử dụng một lá cờ để kiểm tra khi sử dụng trái hoặc đối tác bên phải).

Mặt khác, nếu hiệu suất chuỗi đảo ngược được ghép nối là rất quan trọng, có thể tồn tại một số cấu trúc tối ưu khác hoặc bạn có thể cân nhắc sử dụng @d8d0d65b3f7cf42 strategy.

+0

Tôi không nghĩ rằng bất kỳ hack ma thuật có sẵn cho phụ thêm. Nối các công trình bằng cách lấy phần bên trái của trình tự bên trái và phía bên phải của chuỗi bên phải, và sau đó đệ quy nghiền phần còn lại lại với nhau để tạo thành phần giữa. Nếu bạn đi bên trái của mỗi, đảo ngược một, việc nghiền giữa sẽ nhận được liên tiếp đắt hơn như tiền thu hồi đệ quy. – dfeuer

+0

Đó là lý do tôi nói 'hack', một hack ngớ ngẩn để tránh đảo ngược trên nối có thể được mantain (đối với mỗi concatenacion với ngược lại) một chuỗi các trình tự (thực sự là một cây); các hoạt động ngược lại sẽ được khấu hao (cho đến khi một số hoạt động 'O (n)' được yêu cầu) nhưng các hoạt động tiếp theo (ví dụ 'index') sẽ bị phạt (ví dụ' O (log t * log n) 'thay cho' O (log (t * n)) 'trong đó' t' là các kết nối đang chờ xử lý và 'n' kích thước thân bình thường. Hack không phải là ma thuật ... – josejuan

+0

Hack ngớ ngẩn của tôi được viết trên phản hồi của bạn!" Ý tưởng này đã được thực hiện trong một giấy ở đâu đó "xD xD: P – josejuan

Các vấn đề liên quan