Trouver la somme des distances les plus proches


10

Pour cette tâche, votre code doit prendre deux tableaux triés d'entiers X et Y en entrée. Il doit calculer la somme des distances absolues entre chaque entier dans X et son nombre le plus proche dans Y.

Exemples:

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

La distance est de 2 + 1 + 2.

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

La distance est de 1 + 2 + 3.

Votre code peut être saisi de la manière qui vous convient.

La principale restriction est que votre code doit s'exécuter en temps linéaire dans la somme de la longueur des deux tableaux. . (Vous pouvez supposer que l'ajout de deux entiers prend un temps constant.)


Pouvons-nous utiliser des listes ou des flux au lieu de tableaux?
Ad Hoc Garf Hunter

@CatWizard Oui, vous le pouvez!
Anush

1
Comment est 1 + 2 + 3dérivé de X = (1,2,3)et Y = (0,8)?
guest271314

1
@ guest271314 le nombre le plus proche de deux chacun 1, 2et 3en Yest - 0. Ainsi , les différences sont 1-0, 2-0, 3-0.
dylnan

1
@FreezePhoenix puisque les deux listes sont triées, vous pouvez le faire en O (n + m), car vous parcourez la liste , visitant chaque élément une fois, et tant que vous gardez la trace de l'élément Y j le plus proche de X i , vous pouvez comparer avec Y j et Y j + 1 puisque l'un de ceux-ci est le plus proche de X i + 1XOuijXjeOuijOuij+1Xje+1
Giuseppe

Réponses:


6

Haskell , 70 64 octets

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

Essayez-le en ligne!

Explication

Nous définissons (%)d'abord la différence absolue entre deux nombres. Ensuite, nous définissons (#)comme la fonction intéressante. Dans la première ligne, nous correspondons lorsque les deux listes ne sont pas vides:

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

Sur notre premier cas , d'ici nous engageons dà e:_avec e:_<-d. Cela garantit qu'il dn'est pas vide et définit son premier élément sur e.

Ensuite , si le deuxième élément de ( ) est plus proche que la première ( ) au premier élément de X ( ), nous revenons supprimons le premier élément de Y et d' appeler à nouveau avec le même X .OuiecXax#dOuiX

Si nous correspondons au modèle mais ne remplissons pas la condition que nous faisons:

a%c+b#y

Ce qui supprime le premier élément de et en ajoute la différence absolue du premier élément de X au résultat restant.XX

Enfin, si nous ne correspondons pas au modèle, nous retournons . Le fait de ne pas correspondre au motif signifie que X doit être vide car Y ne peut pas être vide.0XOui

Cet algorithme a la notation d'ordre .O(|X|+|Oui|)

Haskell , 34 octets

Voici comment je le ferais en temps :O(|X|×|Oui|)

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

Essayez-le en ligne!


J'ai précisé dans la question que nous pouvons supposer que l'ajout de deux nombres entiers prend un temps constant.
Anush

2

Python 2 , 124 120 octets

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

Essayez-le en ligne!

4 octets enregistrés en passant au programme par rapport à la fonction.

Il est possible de respecter la contrainte de complexité temporelle car les deux listes sont triées. Notez que chaque fois dans la boucle, soit iest incrémenté soit jincrémenté. Ainsi, la boucle est exécutée la plupart du len(X)+len(Y)temps.


J'ai précisé dans la question que nous pouvons supposer que l'ajout de deux nombres entiers prend un temps constant.
Anush

1

C (gcc), 82 octets

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));}

Cela prend l'entrée comme deux tableaux entiers et leurs longueurs (puisque C n'a aucun moyen d'obtenir leur longueur autrement). Il peut être démontré que cela s'exécute O(a+b)car soit aou best décrémenté à chaque itération de la boucle, qui se termine lorsque aatteint 0(et bne peut pas être décrémenté ci-dessous 0).

Essayez-le en ligne!

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
;}

Quelques notes:

  • Au lieu d'indexer dans les tableaux, l'incrémentation des pointeurs et le déréférencement économisent directement suffisamment d'octets pour que cela en vaille la peine ( *xvs x[a]et y[1]vs y[b+1]).

  • La --b&&condition vérifie de b>1manière détournée - si bc'est le cas 1, elle sera évaluée à zéro. Puisque cela modifie b, nous n'avons pas besoin de le changer dans la première branche du ternaire (qui avance y), mais nous devons le changer de nouveau dans la seconde (qui avance x).

  • Aucune returndéclaration n'est nécessaire, car la magie noire. (Je pense que c'est parce que la dernière instruction à évaluer sera toujours l' n+=...expression, qui utilise le même registre que celui utilisé pour les valeurs de retour.)


0

Rubis, 88 octets

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

Essayez-le en ligne!

De plus, pour le plaisir, une fonction anonyme plus courte qui ne répond pas tout à fait aux restrictions de complexité:

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

Pourriez-vous expliquer en termes simples comment ce code fonctionne? Je ne peux pas dire s'il fonctionne en temps linéaire.
Anush

2
Cela échoue au premier cas de test de la question, ainsi qu'aux entrées telles que [5, 6], [0, 1, 5].
Poignée de porte

0

JavaScript (Node.js) , 80 octets

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
  • Il s'exécute en O (| X | + | Y |): chaque récurrence s'exécute en O (1), et il est récursif | X | + | Y | fois.
    • x, ysont transmis par référence, qui ne copient pas le contenu
  • 1/v est faux si x[i] est hors de portée, véridique sinon
  • t-> d>y[j+1]-v-> v+v>y[j]+y[j+1]est faux tant que les conditions suivantes sont remplies. Et ces moyens y[j]est le nombre le plus proche de vchezy
    • v est inférieur à (y[j]+y[j+1])/2 , ou
    • y[j+1]est hors de portée, ce qui se convertirait NaNet se comparerait àNaN rendementfalse
      • c'est pourquoi nous ne pouvons pas retourner le >signe pour économiser 1 octet de plus
  • test toujours une valeur booléenne, et *convertissez-la en 0/1 avant de calculer

Essayez-le en ligne!


0

Mathematica, 40 octets

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

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

Si vous devez créer un programme complet, avec des entrées:

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

Voici le timing pour jusqu'à 1 000 000 de points (échantillonnés tous les 10 000) pour y:

entrez la description de l'image ici

Proche du linéaire.


1
Cette réponse est un extrait de code car votre entrée est considérée comme des variables préexistantes. Vous devez le reformater pour qu'il s'agisse d'une sous-routine ou d'un programme complet.
Ad Hoc Garf Hunter

Je suis également un peu méfiant que cela fonctionne en temps linéaire, avez-vous une justification pour pourquoi cela devrait? Mathematica a tendance à être plutôt opaque dans la complexité de ses fonctions internes.
Ad Hoc Garf Hunter
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.