2010-01-01 39 views
7

Tengo una lista de números constantes. Necesito encontrar el número más cercano a x en la lista de los números. ¿Alguna idea sobre cómo implementar este algoritmo?Encuentra el número más cercano en una lista de números

+3

Se puede utilizar una lista ordenada? –

+6

No se olvide de considerar los casos inusuales. (1) ¿Qué pasa si no hay un número más cercano? y (2) ¿qué pasa si hay dos números más cercanos diferentes? –

Respuesta

8

Bueno, no puedes hacer esto más rápido que O(N) porque tienes que verificar todos los números para asegurarte de que tienes el más cercano. Dicho esto, ¿por qué no utilizar una variación simple para encontrar el mínimo, buscando el que tenga la diferencia absoluta mínima con x?

Si puede decir que la lista está ordenada desde el principio (y permite el acceso aleatorio, como una matriz), entonces un mejor enfoque es usar una búsqueda binaria. Cuando termine la búsqueda en el índice i (sin encontrar x), solo elija lo mejor de ese elemento y sus vecinos.

+14

Puede vencer a O (N) si su espacio de búsqueda está ordenado. – Paolo

+1

Sí, el uso de la lógica del árbol de búsqueda binaria debería permitirle hacerlo en O (log n). –

+0

O una búsqueda de interpolación, O (log log n) promedio – Malfist

0

Se puede hacer uso de SortedList:
Blog post on finding closest number
Si la complejidad que estás buscando sólo cuenta la búsqueda de la complejidad es O (log (n)). El edificio de la lista costará O (n * log (n))

Si va a insertar un elemento en la lista muchas veces más de lo que va a consultarlo para el número más cercano, entonces la mejor opción es usar List y usar un algoritmo ingenuo para consultar el número más cercano. Cada búsqueda costará O (n) pero el tiempo de inserción se reducirá a O (n).

complejidad general: Si la colección tiene n números y buscó q veces -
Lista: O(n+q*n)
lista ordenada: O(n*log(n)+q*log(n))

Significado, de alguna q del ordenadas lista proporcionará una mejor complejidad.

1
private int? FindClosest(IEnumerable<int> numbers, int x) 
{ 
    return 
     (from number in numbers 
     let difference = Math.Abs(number - x) 
     orderby difference, Math.Abs(number), number descending 
     select (int?) number) 
     .FirstOrDefault(); 
} 

Nulo significa que no había un número más cercano. Si hay dos números con la misma diferencia, elegirá el más cercano a cero. Si dos números están a la misma distancia de cero, se elegirá el número positivo.

Editar en respuesta al comentario de Eric:

Aquí está una versión que tiene la misma semántica, sino que utiliza el operador Min. Requiere una implementación de IComparable<> para que podamos usar Min mientras conservamos el número que va con cada distancia. También lo hice un método de extensión para facilidad de uso:

public static int? FindClosestTo(this IEnumerable<int> numbers, int targetNumber) 
{ 
    var minimumDistance = numbers 
     .Select(number => new NumberDistance(targetNumber, number)) 
     .Min(); 

    return minimumDistance == null ? (int?) null : minimumDistance.Number; 
} 

private class NumberDistance : IComparable<NumberDistance> 
{ 
    internal NumberDistance(int targetNumber, int number) 
    { 
     this.Number = number; 
     this.Distance = Math.Abs(targetNumber - number); 
    } 

    internal int Number { get; private set; } 

    internal int Distance { get; private set; } 

    public int CompareTo(NumberDistance other) 
    { 
     var comparison = this.Distance.CompareTo(other.Distance); 

     if(comparison == 0) 
     { 
      // When they have the same distance, pick the number closest to zero 
      comparison = Math.Abs(this.Number).CompareTo(Math.Abs(other.Number)); 

      if(comparison == 0) 
      { 
       // When they are the same distance from zero, pick the positive number 
       comparison = this.Number.CompareTo(other.Number); 
      } 
     } 

     return comparison; 
    } 
} 
+0

Un uso encantador de LINQ. Sin embargo, observo que esto es O (n lg n) en el tiempo y O (n) en el espacio extra. Puede hacerlo en el tiempo O (n) y en el espacio O (1) si usa el operador de secuencia Min en lugar de ordenar. –

+0

Además, aún no ha eliminado completamente la ambigüedad del enunciado del problema. ¿Qué pasa si hay dos números con la misma diferencia y ambos están igualmente cerca de cero? –

+0

Buen punto. Ordené por 'número descendente' para asegurar que se elegiría el número positivo (en ausencia de un requisito de comportamiento, esa es la decisión más razonable). Trabajaré en la solución 'Min'. Gracias por sus comentarios Eric! –

5

Supongo que la matriz está desordenada. En orden puede ser más rápido

Creo que el método más simple y más rápido es usar algoritmo lineal para encontrar el mínimo o el máximo, pero en lugar de comparar valores, comparará el valor absoluto de la diferencia entre esto y la aguja.

En el C++ (no puedo C#, pero será similar) puede codificar el siguiente aspecto:

// array of numbers is haystack 
// length is length of array 
// needle is number which you are looking for (or compare with) 

int closest = haystack[0]; 
for (int i = 0; i < length; ++i) { 
    if (abs(haystack[ i ] - needle) < abs(closest - needle)) closest = haystack[i]; 
} 
return closest; 
+1

Es "aguja", ¿alguna nomenclatura especial de la que no tengo conocimiento? –

+10

"aguja en el pajar": D –

+1

"aguja" y "pajar" son expresiones que a menudo se usan con los algoritmos de búsqueda. – Gaim

3

En general la gente en este sitio no va a hacer su tarea para usted. Como no has publicado el código, tampoco publicaré el código. Sin embargo, aquí hay un posible enfoque.

Pasa por la lista, restando el número de la lista de x. Tome el valor absoluto de esta diferencia y compárela con el mejor resultado anterior que haya obtenido y, si la diferencia actual es menor que el mejor resultado anterior, guarde el número actual de la lista. Al final del ciclo tendrás tu respuesta.

+2

El OP usó la etiqueta 'homework'. Él no está tratando de tirar uno rápido sobre ti. –

0

ser perezoso no he comprobar esto, pero no debe este trabajo

private int FindClosest(IEnumerable<int> numbers, int x) 
{ 
    return 
     numbers.Aggregate((r,n) => Math.Abs(r-x) > Math.Abs(n-x) ? n 
           : Math.Abs(r-x) < Math.Abs(n-x) ? r 
           : r < x ? n : r); 
} 
0

Haskell:

import Data.List (minimumBy) 
import Data.Ord (comparing) 

findClosest :: (Num a, Ord a) => a -> [a] -> Maybe a 
findClosest _ [] = Nothing 
findClosest n xs = Just $ minimumBy (comparing $ abs . (+ n)) xs 
0
   Performance wise custom code will be more use full. 

       List<int> results; 
       int targetNumber = 0; 
       int nearestValue=0; 
       if (results.Any(ab => ab == targetNumber)) 
       { 
        nearestValue= results.FirstOrDefault<int>(i => i == targetNumber); 
       } 
       else 
       { 
        int greaterThanTarget = 0; 
        int lessThanTarget = 0; 
        if (results.Any(ab => ab > targetNumber)) 
        { 
         greaterThanTarget = results.Where<int>(i => i > targetNumber).Min(); 
        } 
        if (results.Any(ab => ab < targetNumber)) 
        { 
         lessThanTarget = results.Where<int>(i => i < targetNumber).Max(); 
        } 

        if (lessThanTarget == 0) 
        { 
         nearestValue= greaterThanTarget; 
        } 
        else if (greaterThanTarget == 0) 
        { 
         nearestValue= lessThanTarget; 
        } 
        else if (targetNumber - lessThanTarget < greaterThanTarget - targetNumber) 
        { 
         nearestValue= lessThanTarget; 
        } 
        else 
        { 
          nearestValue= greaterThanTarget; 
        } 
       } 
Cuestiones relacionadas