Trova il massimo di ax + b


14

Viene fornito un elenco di ( a, b ) e un elenco di x . Calcola l' ascia massima + b per ogni x . Puoi assumere a , b e x sono numeri interi non negativi.

Il programma o la funzione devono essere eseguiti nel previsto (alla casualità se il codice lo prevede, non l'input) O ( n log n ) tempo in cui n è la lunghezza totale dell'input (somma o massima delle lunghezze di entrambe le liste).

Questo è code-golf. Il codice più corto vince.

Esempio

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

Produzione:

[11 12 14 16 20]

Spiegazione:

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

Nota sulla complessità:

Se hai usato un builtin con una buona complessità nel caso medio, e può essere randomizzato per ottenere facilmente la complessità attesa in teoria, puoi presumere che il tuo linguaggio lo abbia fatto.

Ciò significa che se il tuo programma può essere testato per essere in O ( n log n ), possibilmente con eccezioni di edge case a causa dell'implementazione della tua lingua, ma non può essere visto logicamente nel tuo codice, diremo che è O ( n log n ).


Mi sembra che i risultati attesi dovrebbero essere [11 12 12 15 4]. ???
Bob Jarvis - Ripristina Monica

@BobJarvis È il massimo di ax + b per la corrispondente x, ma per tutti (a, b) nell'elenco. Modificato per rendere l'esempio meno fuorviante.
jimmy23013

lunghezza totale in ingresso = lunghezza delle coppie (a, b) più lunghezza dell'array di x?
Ottimizzatore

@Optimizer Right.
jimmy23013

Perché è chiaro che è persino possibile in O(n log(n))? Potete fornire un algoritmo di riferimento?
flawr

Risposte:


1

Pyth - 99 98 byte

Questa è praticamente una traduzione diretta della risposta Python di @ KeithRandall. Può sicuramente giocare a golf molto di più. Pubblicherò presto una spiegazione .

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

Accetta due elenchi delimitati da virgole, separati da virgole tramite stdin.

Provalo qui

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

Calcola lo scafo convesso ripetendo l'ingresso a,bin aordine crescente . Lo scafo convesso viene registrato Hutilizzando il formato in -1,0,x1,a1,b1,x2,a2,b2,x2,...,xn,an,bncui si xitrova la coordinata x dell'intersezione di a{i-1},b{i-1}e ai,bi.

Quindi eseguo l'iterazione degli input xin ordine, troncando lo scafo convesso per tenere il passo.

Tutto è lineare, tranne per i tipi che sono O (n lgn).

Eseguilo come:

>>> 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: risolto
Keith Randall

@KeithRandall Nell'ultimo passaggio, cerchi Hlinearmente ciascuno xin X, vero ?. Non è possibile che abbiamo una complessità O (n ^ 2) quando entrambe le liste hanno la stessa lunghezza?
coredump

1
@coredump: cerco Hlinearmente per ciascuno x, ma poiché faccio le xs in ordine ricordo dove si è fermata l'ultima ricerca e inizio lì la ricerca successiva. Quindi il whileciclo interno può essere eseguito al massimo O (n) volte su tutto x(anche se potrebbe eseguire O (n) volte per qualsiasi individuo x).
Keith Randall,

@coredump: Nota che accade la stessa cosa per il whileciclo interno nel primo forciclo.
Keith Randall,

@KeithRandall L'ho perso, grazie. Intelligente!
coredump

6

Haskell, 204 271 byte

Modifica : gioca a golf molto di più aggiornando lo scafo convesso come un elenco (ma con la stessa complessità della versione ungolfed), usando "split (x + 1)" invece di "splitLookup x" e rimuovendo tutte le chiamate di funzioni qualificate come Predule. foldl.

Ciò crea una funzione f che prevede l'elenco di coppie (a, b) e un elenco di valori x. Immagino che sarà spazzato via a lungo da qualsiasi cosa nella famiglia APL usando le stesse idee, ma qui va:

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

Esempio di utilizzo:

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

Funziona in O (n log n) tempo; vedi sotto per analisi.

Modifica: ecco una versione non modificata con l'analisi big-O e una descrizione di come funziona tutto:

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 comune - 648 692

Con una vera ricerca binaria.

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

Spiegazione

Sia n la lunghezza di (a, b) e k la lunghezza dei punti.

  • ordina (a, b) per a, quindi b - O (n.ln (n))
  • rimuoviamo le voci per i duplicati a(linee parallele), mantenendo solo la linea parallela con il massimo b, che è sempre maggiore dell'altra (impediamo le divisioni per zero quando calcoliamo le intersezioni) - O (n)
  • comprime il risultato - O (n) : quando abbiamo elementi consecutivi (a0, b0) (a1, b1) (a2, b2) nell'elenco ordinato, in modo tale che il punto di intersezione di (a0, b0) e (a1, b1 ) è maggiore di quello di (a1, b1) e (a2, b2), quindi (a1, b1) può essere tranquillamente ignorato.
  • costruire un elenco di (xab) elementi, dove x è il valore fino a cui la linea ax + b è massima per x (questo elenco è ordinato per x grazie ai passaggi precedenti) - O (n)
  • dato tale elenco, crea un lambda che esegua un controllo dell'intervallo per il suo input e calcoli il valore massimo: l'albero binario è costruito in O (n) (vedi /programming//a/4309901/124319 ). La ricerca binaria che verrà applicata ha complessità O (ln (n)) . Con l'input di esempio, creiamo la seguente funzione (tale funzione viene quindi compilata):

    (LAMBDA (X)
      (IF (< X 4)
          (IF (< X 2)
              (IF (< X -6)
                  (+ (* 0 X) 4)
                  (+ (* 1 X) 10))
              (+ (* 2 X) 8))
          (+ (* 4 X) 0)))
    
  • applica quella funzione per tutti gli elementi - O (k.ln (n))

Complessità risultante: O ((n + k) (ln n))) nel peggiore dei casi.

Non è possibile fornire una stima della complessità per il numero totale di input (n + k), poiché k e n sono indipendenti. Ad esempio, se n è asintoticamente trascurabile rispetto a k , la complessità totale sarebbe O (k) .

Ma se supponiamo che k = O (n) , la complessità risultante è O (n.ln (n)) .

Altri esempi

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

E se spostiamo i backquotes per vedere cosa viene calcolato, possiamo vedere che non abbiamo nemmeno bisogno di fare alcun confronto (una volta che il primo elenco è preelaborato):

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

Ecco un altro esempio (tratto da un commento):

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

La funzione efficace:

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

O (n log n + k) è ovviamente in O ((n + k) log (n + k)).
jimmy23013

Quale interprete stai usando? Ideone(LIST* A B C R) should be a lambda expression.
jimmy23013

@ user23013 Siamo spiacenti, ho dimenticato di (use-package :optima) (modifica ...)
coredump

@ user23013 Temo di non riuscire a far caricare a Ideone una libreria esterna. Per i test, scaricare SBCL (o forse un altro, anche se ho provato solo su SBCL) e installare QuickLisp . Quindi, è possibile (ql: quickload: optima) per scaricare e installare optima. Infine, il codice che ho fornito dovrebbe essere valutabile.
coredump

È tornato (MAPCAR (EVAL (LAMBDA (X) ...che valuta la risposta. Hai lasciato del codice di debug lì?
jimmy23013
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.