최대 ax + b를 구합니다


14

( a, b ) 목록과 x 목록이 제공 됩니다. 각 x에 대한 최대 ax + b 를 계산합니다 . a , bx 가 음이 아닌 정수 라고 가정 할 수 있습니다 .

프로그램이나 기능 (코드가 아닌 입력을 포함하는 경우 무작위로) 예상에서 실행해야합니다 O ( N 로그 N ) 시간 n은 입력 길이의 합계 (합 또는 두 목록의 길이의 최대)입니다.

이것은 코드 골프입니다. 가장 짧은 코드가 승리합니다.

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

산출:

[11 12 14 16 20]

설명:

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

복잡성에 대한 참고 사항 :

평균 대소 문자 복잡도가 좋은 내장을 사용하고 이론상 예상되는 복잡성을 쉽게 얻기 위해 무작위 화 할 수 있다면 언어가 그렇게했다고 가정 할 수 있습니다.

즉, 프로그램을 O ( n log n ) 로 테스트 할 수 있지만 언어 구현으로 인해 예외가 발생하지만 예외적으로 자신의 코드에서 볼 수없는 경우 O (n n log n ).


예상 결과가 [11 12 12 15 4] 인 것 같습니다. ???
밥 자비스-복원 모니카

@BobJarvis 해당 x의 최대 ax + b이지만 목록의 모든 (a, b)입니다. 예제가 덜 오도되도록 변경되었습니다.
jimmy23013

총 입력 길이 = (a, b) 쌍의 길이 + x의 배열 길이
Optimizer

@Optimizer 맞습니다.
jimmy23013

왜 가능하다는 것이 분명 O(n log(n))합니까? 참조 알고리즘을 제공 할 수 있습니까?
flawr

답변:


1

Pyth - 99 98 바이트

이것은 @KeithRandall의 Python 답변에 대한 직접적인 번역입니다. 확실히 더 많이 골프를 칠 수 있습니다. 곧 설명을 게시하겠습니다 .

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

stdin을 통해 쉼표로 구분 된 두 개의 쉼표로 구분 된 목록을 사용합니다.

여기 사용해보십시오

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

파이썬, 214 바이트

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

입력 a,b을 통해 증가하는 a순서 로 반복하여 볼록 껍질을 계산합니다 . 볼록 선체에 기록 H포맷 사용 은 X가 교점의 좌표 와 .-1,0,x1,a1,b1,x2,a2,b2,x2,...,xn,an,bnxia{i-1},b{i-1}ai,bi

그런 다음 입력 x을 정렬 된 순서로 반복 하면서 볼록 껍질을 잘라내면서 계속 진행합니다.

O (n lgn) 인 정렬을 제외한 모든 것이 선형입니다.

다음과 같이 실행하십시오.

>>> 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 : 고정
Keith Randall

@KeithRandall 마지막 단계에서, 당신이 검색 H각 선형 xX, 당신은하지 않습니다? 우리가 가지고 수 없습니다 O (N ^ 2) 두 목록이 같은 길이있을 때 복잡성을?
coredump

1
@coredump : H각각에 대해 선형으로 검색 x하지만, xs를 순서대로 수행하기 때문에 마지막 검색이 중지 된 위치를 기억하고 다음 검색을 시작합니다. 따라서 내부 while루프는 모든 xO (n) 번을 실행할 수 있지만 (모든 개인에 대해 O (n) 번을 실행할 수도 있음 x).
Keith Randall

@coredump : while첫 번째 for루프 의 내부 루프에 대해서도 같은 일이 발생 합니다.
Keith Randall

@KeithRandall 감사합니다. 영리한!
coredump

6

하스켈, 204 271 바이트

편집 : 볼록 껍질을 목록으로 업데이트하지만 (golfed 버전과 동일한 복잡성으로) "splitLookup x"대신 "split (x + 1)"을 사용하고 Predule과 같은 정규화 된 함수 호출을 제거하여 훨씬 더 골프를 쳤다. 폴드.

이것은 (a, b) 쌍의 목록과 x 값의 목록을 기대 하는 함수 f 를 만듭니다 . 나는 같은 아이디어를 사용하여 APL 제품군의 모든 것에 의해 길이 방향으로 날아갈 것이라고 생각하지만 여기에 간다.

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

샘플 사용법 :

[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]

O (n log n) 시간 내에 작동합니다. 분석은 아래를 참조하십시오.

편집 : 다음은 큰 O 분석 및 모든 작동 방식에 대한 설명이 포함 된 ungolfed 버전입니다.

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

커먼 리스프-648 692

실제 이진 검색

(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)

설명

하자 N (a, b)의 길이를하고 케이 점의 길이.

  • (a, b)를 a로 정렬 한 다음 b- O (n.ln (n))
  • a최대 값을 가진 평행선 만 유지 하는 중복 (병렬 선) 항목을 제거 하고 b항상 다른 것보다 큽니다 (교차 계산시 0으로 나누기 방지). -O (n)
  • 결과 압축 -O (n) : (a0, b0)과 (a1, b1의 교차점이되도록 정렬 된 목록에 (a0, b0) (a1, b1) (a2, b2) 요소가 연속 된 경우 )가 (a1, b1) 및 (a2, b2) 중 하나 보다 크면 (a1, b1)을 무시해도됩니다.
  • (xab) 요소의 목록을 작성하십시오. 여기서 x 는 최대 행 ax + bx에 대해 최대 값입니다 (이 목록은 이전 단계에 따라 x 로 정렬 됨 ) -O (n)
  • 해당 목록이 주어지면 입력에 대한 간격 검사를 수행하고 최대 값을 계산하는 람다를 빌드하십시오-이진 트리는 O (n)에 빌드됩니다 ( /programming//a/4309901/124319 참조 ). 적용될 이진 검색에는 O (ln (n)) 복잡성이 있습니다. 예제 입력으로 다음 함수를 빌드합니다 (해당 함수가 컴파일 됨).

    (LAMBDA (X)
      (IF (< X 4)
          (IF (< X 2)
              (IF (< X -6)
                  (+ (* 0 X) 4)
                  (+ (* 1 X) 10))
              (+ (* 2 X) 8))
          (+ (* 4 X) 0)))
    
  • 모든 요소에 해당 함수를 적용 -O (k.ln (n))

결과 복잡성 : O ((n + k) (ln n))) 최악의 시나리오에서 .

kn 은 독립적 이므로 총 입력 수 (n + k)에 대한 복잡성 추정치를 제공 할 수 없습니다 . 예를 들어, n 이 점진적으로 무시할 수있는 wrt k 인 경우 총 복잡도는 O (k) 입니다.

그러나 만약 우리가 가정 K = O (N)를 , 그 생성 된 복잡도는 O (n.ln (N))는 .

다른 예

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

그리고 우리가 계산중인 것을 확인하기 위해 역 따옴표를 옮기면, 우리는 비교할 필요조차 없다는 것을 알 수 있습니다 (첫 번째 목록이 사전 처리되면).

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

다음은 주석에서 가져온 또 다른 예입니다.

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

효과적인 기능 :

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

O (N 로그 N + k)는 물론이고 에서 O ((N + k)는 로그 (N + K)).
jimmy23013

어떤 통역사를 사용하고 있습니까? Ideone이 제공합니다 (LIST* A B C R) should be a lambda expression.
jimmy23013

@ user23013 죄송합니다. (use-package :optima) (편집 중)을 잊어 버렸습니다
coredump

@ user23013 Ideone이 외부 라이브러리를로드 할 수 없게되어 있습니다. 테스트를 위해 SBCL (또는 SBCL에서만 테스트되었지만 다른 것)을 다운로드하고 quicklisp을 설치하십시오 . 그런 다음 (ql : quickload : optima)를 다운로드하여 설치할 수 optima있습니다. 마지막으로, 내가 제공 한 코드는 평가 가능해야합니다.
coredump

(MAPCAR (EVAL (LAMBDA (X) ...답변으로 평가되는 것이 반환 되었습니다. 거기에 디버그 코드를 남겼습니까?
jimmy23013
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.