2011-11-30 26 views
7

Tôi muốn xác định một hàmHaskell: đảo ngược một hoán vị trong thời gian tuyến tính, sử dụng chỉ liệt kê

invert :: [Int] -> [Int] 

rằng giả định rằng đầu vào của nó là một hoán vị của [0..(n-1)], và trả về nghịch đảo của nó. Có thể xác định nó bằng cách sử dụng chỉ danh sách và bộ dữ liệu (không có mảng) để nó chạy trong thời gian tuyến tính?

Điều này chủ yếu là vì lợi ích học tập; trong mã thực, tôi có thể sử dụng Array hoặc STArray hoặc tương tự.

+0

bạn có thể giải thích một nghịch của một hoán vị là những gì danh sách? – Tarrasch

+0

Tarrasch, có một cái nhìn ở đây: http://mathworld.wolfram.com/InversePermutation.html – Kenji

+1

Tôi thích câu hỏi này, nhưng tôi tin rằng nó không thể làm điều đó trong O (n). Một phương pháp đơn giản yêu cầu sắp xếp. Nếu danh sách Haskell hỗ trợ lập chỉ mục O (1) và phụ thêm, thì nó sẽ là có thể. Nhiều thao tác trong 'Data.List' là O (n), vì vậy chúng tôi rất hạn chế về hiệu quả. – Kenji

Trả lời

2

Không chắc chắn về thời gian tuyến tính, chỉ là ghi chú mới bắt đầu.

λ> (\x -> map snd $ sort $ zip x [1..(length x)]) [3,8,5,10,9,4,6,1,7,2] 
[8,10,1,6,3,7,9,2,5,4] 
+0

Điều đó sẽ đủ điều kiện nếu bạn có thể cung cấp thuật toán phân loại 'O (n)' ... – leftaroundabout

+0

Cảm ơn, nhưng điều này sử dụng một loại so sánh, vì vậy nó sẽ là Ω (n log n). – Prateek

+7

Nhân tiện, bạn có thể bỏ qua 'length x', chỉ cần zip với' [1 ..] '. – leftaroundabout

1

Dường như với tôi rằng bạn không thể làm điều này trong thời gian tuyến tính. Để thực hiện với độ phức tạp thời gian O (n), bạn sẽ cần phải tạo danh sách kết quả theo thứ tự mà bạn không thể thực hiện trực tiếp với các danh sách khuyết điểm, tôi giả sử.

+1

Tôi cũng muốn tin điều đó. Nhưng sau đó, việc thực hiện đệ quy "rõ ràng" của "đảo ngược" là thời gian bậc hai, và có một quá trình thực hiện thời gian tuyến tính dễ dàng. Hãy xem xét 'minout :: [Int] -> Int' trong đó có một danh sách các số nguyên không phân biệt riêng biệt và trả về số nguyên không âm nhỏ nhất * không * có trong đầu vào. Điều này cũng có thể được thực hiện trong thời gian tuyến tính, với một số thông minh. Vì vậy, tôi tự hỏi ... – Prateek

+0

@prateek: Tôi không chắc liệu tôi có quan điểm của bạn hay không. Tất nhiên, 'minout' có một thời gian thực hiện tuyến tính rõ ràng; Tôi thậm chí còn nói rằng điều đó không cần quá nhiều thông minh: 'minout = foldr (max. Succ) 0'. Nhưng điều này liên quan đến 'đảo ngược' như thế nào?Sự khác biệt giữa 'đảo ngược' và 'nghịch đảo' là trước đây có thể xây dựng kết quả của nó theo thứ tự, trong khi cái sau — tôi tin là không thể. –

+0

Điểm của tôi (hơi yếu) chỉ là những thứ khó làm với các danh sách trong thời gian tuyến tính có thể là có thể, vì vậy có lẽ có một cách thông minh để "đảo ngược" mà tôi chưa từng nghĩ tới. Tôi không biết làm thế nào người ta có thể chứng minh một kết quả tiêu cực ở đây. (Bằng cách này, hàm bạn đã cung cấp không tính toán 'minout' như tôi đã định nghĩa nó - nó tính 1 + phần tử tối đa của danh sách:' minout [0,2,4] 'nên là' 1') – Prateek

2

Vì vậy, điều này không sử dụng "danh sách duy nhất". Nhưng nó có vẻ vừa vặn.

import qualified Data.Vector as V 

invert :: [Int] -> [Int] 
invert list = V.toList $ vec V.// assocs 
    where vec = V.fromList list -- better ideas for initializing vec? 
     assocs = zip (map pred list) [1..] 

Xem the Vector package, mà tuyên bố rằng //O (n). Vâng, nó nói O (n + m), nhưng trong trường hợp này là n = m.

Tôi tải nó lên thành ghci và nhận được câu trả lời giống như dmitry. :)

+2

Chỉ cần nghĩ về Vectors như thực sự lớn tuples. ;) –

+0

[Thực hiện thay thế bằng cách sử dụng khởi tạo monad] (http://stackoverflow.com/a/6737456/98117). (Lưu ý: điều này sử dụng chỉ mục dựa trên zero) – hammar

0

Nếu hoán vị được thể hiện dưới dạng danh sách các chu kỳ rời nhau, sau đó

invert :: [[Int]] -> [[Int]] 
invert = map reverse 

chạy trong thời gian tuyến tính. Tôi không chắc chắn nếu có một cách tuyến tính để chuyển đổi qua lại giữa các đại diện khác nhau, mặc dù.

1

Như có vẻ không phải là một câu trả lời tích cực cho câu hỏi thực tế, tôi sẽ thêm cheat của tôi vào danh sách các phi giải pháp:

invert :: [Int] -> [Int] 
invert lst = take lng $ map (fromIntegral.(`mod`h)) $ iterate (`div`h) 
       $ sum $ zipWith (\k x->k*h^x) [0..] lst 
    where h::Integer 
     h = fromIntegral lng 
     lng = length lst 
+0

Tôi tự hỏi tại điểm nào 'k * h^x' trở thành một yếu tố suy giảm đáng kể. –

+0

Khá sớm, thật không may! Như tôi đã nói, nó không thực sự là một giải pháp; trên thực tế, lũy thừa làm cho nó chậm hơn nhiều so với _O_ (_n_ log _n_), giống như _O_ (_n_ ² log _n_). – leftaroundabout

+0

Nó sẽ là _O_ (_n_) trên một số máy tính lượng tử giả định với số mũ có độ chính xác tùy ý, mặc dù ... – leftaroundabout

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