Trova la somma delle distanze più vicine


10

Per questa attività il codice dovrebbe prendere due matrici ordinate di numeri interi X e Y come input. Dovrebbe calcolare la somma delle distanze assolute tra ciascun numero intero in X e il suo numero più vicino in Y.

Esempi:

X = (1 5,9)
Y = (3,4,7)

La distanza è 2 + 1 + 2.

X = (1,2,3)
Y = (0,8)

La distanza è 1 + 2 + 3.

Il tuo codice può ricevere input in qualsiasi modo sia conveniente.

La restrizione principale è che il codice deve essere eseguito in tempo lineare nella somma della lunghezza dei due array. . (Si può presumere che l'aggiunta di due numeri interi richieda un tempo costante.)


Possiamo usare elenchi o stream invece di array?
— Ad Hoc Garf Hunter

@CatWizard Sì, puoi!
— Anush,

1
Come viene 1 + 2 + 3derivato da X = (1,2,3)e Y = (0,8)?
— guest271314

1
@ guest271314 più vicino numero due ciascuno dei 1, 2ed 3in YIS 0. Così le differenze sono 1-0, 2-0, 3-0.
— dylnan,

1
@FreezePhoenix poiché entrambi gli elenchi sono ordinati, è possibile farlo in O (n + m), perché si scorre l'elenco , visitando ogni elemento una volta e finché si tiene traccia dell'elemento Y j più vicino a X i , è possibile controlla contro Y j e Y j + 1 poiché uno di questi è il più vicino a X i + 1XYjXioYjYj+1Xio+1
— Giuseppe

Risposte:


6

Haskell , 70 64 byte

a%b=abs$a-b
x@(a:b)#y@(c:d)|e:_<-d,a%c>a%e=x#d|1>0=a%c+b#y
_#_=0

Provalo online!

Spiegazione

Per prima cosa definiamo (%)la differenza assoluta tra due numeri. Quindi definiamo (#)la funzione interessante. Nella prima riga corrispondiamo quando entrambe le liste non sono vuote:

x@(a:b)#(c:d:e)

Al nostro primo caso da qui ci leghiamo da e:_con e:_<-d. Questo assicura che dnon sia vuoto e imposti il ​​suo primo elemento su e.

Quindi se il secondo elemento di ( ) è più vicino del primo ( ) al primo elemento di X ( ), si ritorna rimozione del primo elemento di Y e chiamando nuovamente con la stessa X .YecXax#dYX

Se abbiniamo lo schema ma non superiamo la condizione che facciamo:

a%c+b#y

Il che rimuove il primo elemento di e aggiunge la differenza assoluta dal primo elemento di X al risultato rimanente.XX

Infine, se non corrispondiamo al modello, restituiamo . Non corrispondere al modello significa che X deve essere vuoto perché Y non può essere vuoto.0XY

Questo algoritmo ha la notazione dell'ordine .O(|X|+|Y|)

Haskell , 34 byte

Ecco come lo farei in volta:O(|X|×|Y|)

x#y=sum[minimum$abs.(z-)<$>y|z<-x]

Provalo online!


Ho chiarito nella domanda che possiamo supporre che l'aggiunta di due numeri interi richieda un tempo costante.
— Anush,

2

Python 2 , 124 120 byte

X,Y=input()
i=j=s=0
while i<len(X):
 v=abs(Y[j]-X[i])
 if j+1<len(Y)and v>=abs(Y[j+1]-X[i]):j+=1
 else:s+=v;i+=1
print s

Provalo online!

Salvati 4 byte passando al programma rispetto alla funzione.

Soddisfare il vincolo di complessità temporale è possibile perché entrambi gli elenchi sono ordinati. Si noti che ogni volta intorno al ciclo, iviene incrementato o jincrementato. Pertanto il loop viene eseguito il più delle len(X)+len(Y)volte.


Ho chiarito nella domanda che possiamo supporre che l'aggiunta di due numeri interi richieda un tempo costante.
— Anush,

1

C (gcc), 82 byte

n;f(x,y,a,b)int*x,*y;{for(n=0;a;)--b&&*x*2-*y>y[1]?++y:(++b,--a,n+=abs(*x++-*y));}

Questo prende input come due array interi e le loro lunghezze (poiché C non ha modo di ottenere la loro lunghezza altrimenti). Questo può essere eseguito O(a+b)perché ao bè decrementato su ogni iterazione del ciclo, che termina quando araggiunge 0(e bnon può essere decrementato di seguito 0).

Provalo online!

n;                     // define sum as an integer
f(x,y,a,b)             // function taking two arrays and two lengths
int*x,*y;              // use k&r style definitions to shorten function declaration
{
 for(n=0;              // initialize sum to 0
 a;)                   // keep looping until x (the first array) runs out
                       // we'll decrement a/b every time we increment x/y respectively
 --b&&                 // if y has ≥1 elements left (b>1, but decrements in-place)...
 *x*2-*y>y[1]?         // ... and x - y > [next y] - x, but rearranged for brevity...
 ++y:                  // increment y (we already decremented b earlier);
 (++b,                 // otherwise, undo the in-place decrement of b from before...
 --a,n+=abs(*x++-*y))  // decrement a instead, add |x-y| to n, and then increment x
;}

Alcune note:

  • Invece di indicizzare nelle matrici, incrementare i puntatori e la dereferenziazione salva direttamente abbastanza byte per valerne la pena ( *xvs x[a]e y[1]vs y[b+1]).

  • La --b&&condizione verifica b>1in modo rotatorio - se lo bè 1, verrà valutata a zero. Poiché questo modifica b, non abbiamo bisogno di cambiarlo nel primo ramo del ternario (che avanza y), ma dobbiamo cambiarlo nel secondo (che avanza x).

  • Non returnè necessaria alcuna dichiarazione, perché la magia nera. (Penso che sia perché l'ultima istruzione da valutare sarà sempre l' n+=...espressione, che utilizza lo stesso registro di quello usato per i valori di ritorno.)


0

Rubino, 88 byte

->(p,q){a=q.each_cons(2).map{|a|a.sum/a.size}
x=a[0]
p.sum{|n|x=a.pop if n>x
(n-x).abs}}

Provalo online!

Inoltre, per divertimento, una funzione anonima più breve che non soddisfa del tutto le restrizioni di complessità:

->(a,b){a.map{|x|x-b.min_by{|y|(x-y).abs}}.sum}

Potresti spiegare in termini semplici come funziona questo codice? Non so dire se funziona in tempo lineare.
— Anush,

2
Questo fallisce il primo caso di test nella domanda, così come input come [5, 6], [0, 1, 5].
— Maniglia della porta

0

JavaScript (Node.js) , 80 byte

x=>g=(y,i=0,j=0,v=x[i],d=v-y[j],t=d>y[j+1]-v)=>1/v?g(y,i+!t,j+t)+!t*(d>0?d:-d):0
  • Funziona in O (| X | + | Y |): ogni ricorsione viene eseguita in O (1) e ricorsiva | X | + | Y | volte.
    • x, yvengono passati per riferimento, che non copia il contenuto
  • 1/vè falso se x[i]è fuori portata, in verità altrimenti
  • t-> d>y[j+1]-v-> v+v>y[j]+y[j+1]è falso purché siano soddisfatte le seguenti condizioni. E ciò significa che y[j]è il numero più vicino a viny
    • vè inferiore a (y[j]+y[j+1])/2, o
    • y[j+1]non rientra nell'intervallo, che verrebbe convertito in NaN, e confrontato con il NaNrendimentofalse
      • ecco perché non possiamo capovolgere il >segno per salvare un altro byte
  • tè sempre un valore booleano e lo *converte in 0/ 1prima del calcolo

Provalo online!


0

Mathematica, 40 byte

x = {1, 5, 9};
y = {3, 4, 7};

Norm[Flatten[Nearest[y] /@ x] - x]

Se è necessario creare un programma completo, con input:

f[x_,y_]:= Norm[Flatten[Nearest[y] /@ x] - x]

Ecco i tempi per un massimo di 1.000.000 di punti (campionati ogni 10.000) per y:

inserisci qui la descrizione dell'immagine

Vicino a lineare.


1
Questa risposta è uno snippet di codice poiché il tuo input viene preso come variabili preesistenti. Dovresti riformattarlo in una sub-routine o in un programma completo.
— Ad Hoc Garf Hunter

Sono anche un po 'sospettoso che funzioni in tempo lineare, hai qualche giustificazione sul perché dovrebbe? Mathematica tende ad essere piuttosto opaca nella complessità dei suoi builtin.
— Ad Hoc Garf Hunter
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.