Tìm cực đại của ax + b


14

Bạn được cung cấp một danh sách ( a, b ) và danh sách x . Tính axe tối đa + b cho mỗi x . Bạn có thể giả sử a , bx là các số nguyên không âm.

Chương trình hoặc chức năng của bạn phải chạy trong dự kiến ​​(tính ngẫu nhiên nếu mã của bạn liên quan đến điều đó, không phải thời gian đầu vào) O ( n log n ) trong đó n là tổng độ dài đầu vào (tổng hoặc tối đa độ dài của cả hai danh sách).

Đây là môn đánh gôn. Mã ngắn nhất sẽ thắng.

Thí dụ

[[2 8] [4 0] [2 1] [1 10] [3 3] [0 4]] [1 2 3 4 5]

Đầu ra:

[11 12 14 16 20]

Giải trình:

11 = 1*1 + 10
12 = 1*2 + 10 = 2*2 + 8
14 = 2*3 + 8
16 = 2*4 + 8 = 4*4 + 0
20 = 4*5 + 0

Lưu ý về sự phức tạp:

Nếu bạn đã sử dụng một nội dung có độ phức tạp trường hợp trung bình tốt và nó có thể được chọn ngẫu nhiên để có được độ phức tạp dự kiến ​​một cách dễ dàng trong lý thuyết, bạn có thể giả sử ngôn ngữ của bạn đã làm điều đó.

Điều đó có nghĩa là, nếu chương trình của bạn có thể được kiểm tra ở dạng O ( n log n ), có thể có ngoại lệ trường hợp cạnh do việc triển khai ngôn ngữ của bạn, nhưng không thể được nhìn thấy một cách logic trong mã của riêng bạn, chúng tôi sẽ nói đó là O ( n log n ).


Theo tôi thì kết quả mong đợi sẽ là [11 12 12 15 4]. ???
Bob Jarvis - Tái lập Monica

@BobJarvis Đó là tối đa của ax + b cho x tương ứng, nhưng với tất cả (a, b) trong danh sách. Thay đổi để làm cho ví dụ ít gây hiểu lầm.
jimmy23013

tổng chiều dài đầu vào = chiều dài của cặp (a, b) cộng với độ dài của mảng x?
Tối ưu hóa

@Optimizer Đúng.
jimmy23013

Tại sao rõ ràng là nó thậm chí có thể trong O(n log(n))? Bạn có thể cung cấp một thuật toán tham khảo?
flawr 8/03/2015

Câu trả lời:


1

Pyth - 99 98 byte

Đây là bản dịch trực tiếp câu trả lời Python của @ KeithRandall. Nó chắc chắn có thể được chơi golf nhiều hơn nữa. Tôi sẽ đăng một lời giải thích sớm .

K[_1Z;FNShQAkdNW&>K2>+*k@K_3d+*@K_2@K_3eK=K<K_3)~K[c-eKd-k@K_2kd;FNSeQW&>K2>N@K2=K>K3)aY+*hKN@K1;Y

Có hai danh sách được phân cách bằng dấu phẩy, được phân tách bằng dấu phẩy thông qua stdin.

Hãy thử nó ở đây

K                  K=
 [  )              A List containing
  _1               Negative 1
  Z                Zero
FN                 For N in
 ShQ               Sorted first input
Akd                Double assign k and d
 N                 To N
 W                 While
  &                Logical And
   >K2             K>2
   >               Greater Than
    +*k@K_3d       K[-3]*k+d
    +              Plus
     *@K_2@K_3     K[-2]*K[-3]
     eK            K[-1]
  =K               K=
   <K_3            K[:-3]
  )                Close while loop
 ~K                K+=
  [      )         List constructor
   c               Float division
    -              Minus
     eK            K[-1]
     d             d
    -              Minus
     k             k
     @K_2          K[-2]
   k               k
   d               d
 ;                 End list and for loop
FN                 For N in
  SeQ              Sorted second input
  W                While loop
   &               Logical and
    >K2            K[2:]
    >              Greater than
     N             N
     @K2           K[2]
   =K              K=
   >K3             K[3:]
  )                Close while loop
  aY               Y.append - Y is empty list
   +               Plus
    *hKN           (K+1)*N
    @K1            K[1]
;                  Close out everything
Y                  Print Y

10

Python, 214 byte

S=sorted
def M(L,X):
 H=[-1,0];R={}
 for a,b in S(L):
    while H[2:]and a*H[-3]+b>H[-2]*H[-3]+H[-1]:H=H[:-3]
    H+=[1.*(H[-1]-b)/(a-H[-2]),a,b]
 for x in S(X):
    while H[2:]and x>H[2]:H=H[3:]
    R[x]=H[0]*x+H[1]
 return R

Tính toán vỏ lồi bằng cách lặp qua đầu vào a,btheo athứ tự tăng dần . Vỏ lồi được ghi lại Hbằng cách sử dụng định dạng -1,0,x1,a1,b1,x2,a2,b2,x2,...,xn,an,bntrong đó xitọa độ x của giao điểm của a{i-1},b{i-1}ai,bi.

Sau đó, tôi lặp đi lặp lại mặc dù các đầu vào xs theo thứ tự được sắp xếp, cắt ngắn thân lồi để theo kịp khi tôi đi.

Tất cả mọi thứ là tuyến tính ngoại trừ các loại là O (n lgn).

Chạy nó như:

>>> print M([[2,8],[4,0],[2,1],[1,10],[3,3],[0,4]], [1,2,3,4,5])
{1: 11, 2: 12, 3: 14, 4: 16, 5: 20}

@ user23013: đã sửa
Keith Randall

@KeithRandall Trong bước cuối cùng, bạn tìm kiếm trong Htuyến tính cho mỗi xtrong X, phải không ?. Không phải chúng ta có độ phức tạp O (n ^ 2) khi cả hai danh sách có cùng độ dài sao?
coredump

1
@coredump: Tôi tìm kiếm Htuyến tính cho từng loại x, nhưng vì tôi thực hiện xtheo thứ tự nên tôi nhớ nơi tìm kiếm cuối cùng dừng lại và bắt đầu tìm kiếm tiếp theo ở đó. Vì vậy, whilevòng lặp bên trong có thể thực thi tối đa O (n) lần trên tất cả x(mặc dù nó có thể thực thi O (n) lần cho bất kỳ cá nhân nào x).
Keith Randall

@coredump: Lưu ý rằng điều tương tự xảy ra với whilevòng lặp bên trong trong vòng lặp đầu tiên for.
Keith Randall

@KeithRandall Tôi đã bỏ lỡ điều đó, cảm ơn. Tài giỏi!
coredump

6

Haskell, 204 271 byte

Chỉnh sửa : chơi gôn nhiều hơn nữa bằng cách cập nhật vỏ lồi dưới dạng danh sách (nhưng có độ phức tạp tương tự như phiên bản không được chỉnh sửa), sử dụng "split (x + 1)" thay vì "splitLookup x" và xóa tất cả các lệnh gọi hàm đủ điều kiện như Predule. nếp gấp.

Điều này tạo ra một hàm f dự kiến ​​danh sách các cặp (a, b) và danh sách các giá trị x. Tôi đoán nó sẽ bị thổi bay theo chiều dài bởi bất cứ thứ gì trong gia đình APL sử dụng cùng một ý tưởng, nhưng rồi đây:

import Data.Map
r=fromListWith max
[]%v=[(0,v)]
i@((p,u):j)%v|p>v#u=j%v|0<1=(v#u,v):i
(a,b)#(c,d)=1+div(b-d)(c-a)
o i x=(\(a,b)->a*x+b)$snd$findMax$fst$split(x+1)$r$foldl'(%)[]$r$zip(fmap fst i)i
f=fmap.o

Sử dụng mẫu:

[1 of 1] Compiling Main             ( linear-min.hs, interpreted )
Ok, modules loaded: Main.
λ> f [(2,8), (4,0), (2,1), (1,10), (3,3), (0,4)] [1..5]
[11,12,14,16,20]
λ> f [(1,20), (2,12), (3,11), (4,8)] [1..5]
[21,22,23,24,28]

Nó hoạt động trong thời gian O (n log n); xem bên dưới để phân tích.

Chỉnh sửa: Đây là phiên bản chưa được chỉnh sửa với phân tích big-O và mô tả về cách thức hoạt động của tất cả:

import Prelude hiding (null, empty)
import Data.Map hiding (map, foldl)

-- Just for clarity:
type X = Int
type Y = Int
type Line = (Int,Int)
type Hull = Data.Map.Map X Line
slope (a,b) = a

{-- Take a list of pairs (a,b) representing lines a*x + b and sort by
    ascending slope, dropping any lines which are parallel to but below
    another line.

    This composition is O(n log n); n for traversing the input and
    the output, log n per item for dictionary inserts during construction.
    The input and output are both lists of length <= n.
--}
sort :: [Line] -> [Line]
sort = toList . fromListWith max

{-- For lines ax+b, a'x+b' with a < a', find the first value of x
    at which a'x + b' exceeds ax + b. --}
breakEven :: Line -> Line -> X
breakEven p@(a,b) q@(a',b') = if slope p < slope q
                                 then 1 + ((b - b') `div` (a' - a))
                                 else error "unexpected ordering"

{-- Update the convex hull with a line whose slope is greater
    than any other lines in the hull.  Drop line segments
    from the hull until the new line intersects the final segment.
    split is used to find the portion of the convex hull
    to the right of some x value; it has complexity O(log n).
    insert is also O(log n), so one invocation of this 
    function has an O(log n) cost.

    updateHull is recursive, but see analysis for hull to
    account for all updateHull calls during one execution.
--}
updateHull :: Line -> Hull -> Hull
updateHull line hull
    | null hull = singleton 0 line
    | slope line <= slope lastLine = error "Hull must be updated with lines of increasing slope"
    | hull == hull' = insert x line hull
    | otherwise = updateHull line hull''
    where (lastBkpt, lastLine) = findMax hull
          x = breakEven lastLine line
          hull' = fst $ x `split` hull
          hull'' = fst $ lastBkpt `split` hull

{-- Build the convex hull by adding lines one at a time,
    ordered by increasing slope.

    Each call to updateHull has an immediate cost of O(log n),
    and either adds or removes a segment from the hull. No
    segment is added more than once, so the total cost is
    O(n log n).
--}
hull :: [Line] -> Hull
hull = foldl (flip updateHull) empty . sort

{-- Find the highest line for the given x value by looking up the nearest
    (breakpoint, line) pair with breakpoint <= x.  This uses the neat
    function splitLookup which looks up a key k in a dictionary and returns
    a triple of:
        - The subdictionary with keys < k.
        - Just v if (k -> v) is in the dictionary, or Nothing otherwise
        - The subdictionary with keys > k.

    O(log n) for dictionary lookup.
--}
valueOn :: Hull -> X -> Y
valueOn boundary x = a*x + b
    where (a,b) = case splitLookup x boundary of
                    (_  , Just ab, _) -> ab
                    (lhs,       _, _) -> snd $ findMax lhs


{-- Solve the problem!

    O(n log n) since it maps an O(log n) function over a list of size O(n).
    Computation of the function to map is also O(n log n) due to the use
    of hull.
--}
solve :: [Line] -> [X] -> [Y]
solve lines = map (valueOn $ hull lines)

-- Test case from the original problem
test = [(2,8), (4,0), (2,1), (1,10), (3,3), (0,4)] :: [Line]
verify = solve test [1..5] == [11,12,14,16,20]

-- Test case from comment
test' = [(1,20),(2,12),(3,11),(4,8)] :: [Line]
verify' = solve test' [1..5] == [21,22,23,24,28]

2

Lisp thường gặp - 648 692

Với một tìm kiếm nhị phân thực tế.

(use-package :optima)(defun z(l e)(labels((i(n m)(/(-(cadr m)(cadr n))(-(car n)(car m))))(m(l)(match l((list* a b c r)(if(<(i a b)(i b c))(list* a(m(list* b c r)))(m(list* a c r))))(_ l)))(f(x &aux(x(cdr x)))`(+(*,(car x)x),(cadr x)))(g(s e)(let*((q(- e s))(h(+ s(floor q 2)))d)(if(> q 1)(let((v(g s h))(d(pop l)))`(if(< x,(car d)),v,(g(1+ h)e)))(cond((not(car (setq d (pop l))))(f d))((> q 0)`(if(< x,(car d)),(f d),(f(pop l))))(t(f d)))))))(setq l(loop for(a b)on(m(remove-duplicates(#3=stable-sort(#3# l'< :key'cadr)'< :key'car):key 'car)) by #'cdr collect`(,(when b(i a b)),(car a),(cadr a))))`(mapcar(eval(lambda(x),(g 0(1-(length l)))))',e)))

(z '((2 8) (4 0) (2 1) (1 10) (3 3) (0 4)) '(1 2 3 4 5))
=> (11 12 14 16 20)

Giải trình

Gọi n là độ dài của (a, b) và k chiều dài của các điểm.

  • sắp xếp (a, b) theo a, sau đó b - O (n.ln (n))
  • xóa các mục nhập trùng lặp a(các đường song song), chỉ giữ lại đường song song với mức tối đa b, luôn luôn lớn hơn các mục khác (chúng tôi ngăn chia cho 0 khi tính toán các giao điểm) - O (n)
  • nén kết quả - O (n) : khi chúng ta có các phần tử liên tiếp (a0, b0) (a1, b1) (a2, b2) trong danh sách được sắp xếp, sao cho điểm giao nhau của (a0, b0) và (a1, b1 ) lớn hơn một trong số (a1, b1) và (a2, b2), sau đó (a1, b1) có thể được bỏ qua một cách an toàn.
  • xây dựng danh sách các phần tử (xab), trong đó x là giá trị tối đa của dòng ax + b là tối đa cho x (danh sách này được sắp xếp theo x nhờ các bước trước) - O (n)
  • đưa ra danh sách đó, xây dựng lambda thực hiện kiểm tra khoảng thời gian cho đầu vào của nó và tính giá trị tối đa - cây nhị phân được xây dựng trong O (n) (xem /programming//a/4309901/124319 ). Tìm kiếm nhị phân sẽ được áp dụng có độ phức tạp O (ln (n)) . Với đầu vào ví dụ, chúng tôi xây dựng hàm sau (hàm đó được biên dịch):

    (LAMBDA (X)
      (IF (< X 4)
          (IF (< X 2)
              (IF (< X -6)
                  (+ (* 0 X) 4)
                  (+ (* 1 X) 10))
              (+ (* 2 X) 8))
          (+ (* 4 X) 0)))
    
  • áp dụng hàm đó cho tất cả các phần tử - O (k.ln (n))

Kết quả phức tạp: O ((n + k) (ln n))) trong trường hợp xấu nhất.

Chúng tôi không thể cung cấp ước tính phức tạp cho tổng số đầu vào (n + k), vì kn là độc lập. Ví dụ, nếu n là wrt k không có triệu chứng , thì tổng độ phức tạp sẽ là O (k) .

Nhưng nếu chúng ta giả sử rằng k = O (n) , thì độ phức tạp kết quả là O (n.ln (n)) .

Những ví dụ khác

(z '((1 10) (1 8) (1 7)) '(1 2 3 4 5))
=> (11 12 13 14 15)

Và nếu chúng ta di chuyển backquote để xem những gì đang được tính toán, chúng ta có thể thấy rằng chúng ta thậm chí không cần thực hiện bất kỳ so sánh nào (một khi danh sách đầu tiên được xử lý trước):

(MAPCAR (LAMBDA (X) (+ (* 1 X) 10)) '(1 2 3 4 5))

Đây là một ví dụ khác (lấy từ một bình luận):

(z '((1 20) (2 12) (3 11) (4 8)) '(1 2 3 4 5))
=> (21 22 23 24 28)

Chức năng hiệu quả:

(MAPCAR
  (LAMBDA (X)
    (IF (< X 4)
        (+ (* 1 X) 20)
        (+ (* 4 X) 8)))
  '(1 2 3 4 5))

O (n log n + k) là tất nhiên trong O ((n + k) log (n + k)).
jimmy23013

Bạn đang sử dụng thông dịch viên nào? Ideone cho (LIST* A B C R) should be a lambda expression.
jimmy23013

@ user23013 Xin lỗi, tôi đã quên (use-package :optima) (chỉnh sửa ...)
coredump

@ user23013 Tôi e rằng tôi không thể tải Ideone một thư viện bên ngoài. Để thử nghiệm, vui lòng tải xuống SBCL (hoặc có thể khác, mặc dù tôi chỉ thử nghiệm trên SBCL) và cài đặt quicklisp . Sau đó, bạn có thể (ql: quickload: optima) để tải xuống và cài đặt optima. Cuối cùng, mã tôi cung cấp phải được đánh giá.
coredump

Nó trả về (MAPCAR (EVAL (LAMBDA (X) ...mà đánh giá cho câu trả lời. Bạn đã để lại một số mã gỡ lỗi ở đó?
jimmy23013
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.