2011-10-23 8 views
6

tengo una lista de valores dobles, que desea redondear el valor de una variable a solamente esa lista de númerosCompletando un valor de solamente una lista de ciertos valores en C#

Ejemplo:

Los contenidos de la lista son : 12,15,23,94,35,48 {}

valor de la variable es 17, por lo que se redondeará a 15

Si el valor de la variable es menor que el menor número, se redondeará para ello, si su valor es mayor que el número más grande, se redondeará a él.

El contenido de la lista siempre cambia según un factor externo, por lo que no puedo hacer referencia a los valores que quiero redondear hacia arriba o hacia abajo.

¿Cómo puedo hacerlo en C#?

+0

¿Se puede usar LINQ? – Yuck

+0

Resuelva la diferencia entre los valores no redondeados y los posibles resultados. Almacene estas diferencias en un diccionario marcado por el posible resultado y seleccione el resultado con la diferencia más baja. – Keyo

+2

¿Qué tan grande puede ser esta colección? De lo contrario, debería considerar usar una colección ordenada y usar la búsqueda binaria. –

Respuesta

8

Aquí es un método que utiliza LINQ:

var list = new[] { 12, 15, 23, 94, 35, 48 }; 
var input = 17; 

var diffList = from number in list 
       select new { 
        number, 
        difference = Math.Abs(number - input) 
       }; 
var result = (from diffItem in diffList 
       orderby diffItem.difference 
       select diffItem).First().number; 

EDITAR: Ahora se llama algunas de las variables por lo que el código es menos confuso ...

EDITAR:

La variable list es una matriz de declaración implícita de int. La primera instrucción LINQ diffList define un tipo anónimo que tiene su número original de la lista (number), así como la diferencia entre este y su valor actual (input).

La segunda instrucción LINQ result ordena esa recopilación de tipo anónimo por la diferencia, que es su requisito de "redondeo". Toma el primer elemento de esa lista, ya que tendrá la menor diferencia, y luego selecciona solo el .number original del tipo anónimo.

+0

+1. Es más eficiente que mi solución ya que solo bucles 'valores' una vez. –

+0

Funciona como un amuleto, aunque no puedo entenderlo todo :) – SKandeel

+0

Muchas gracias por la explicación ... – SKandeel

7

Suponiendo que la matriz se ordena, se podría realizar una binary search en la matriz, reducir la lista a la que dos números el número dado se encuentra entre.

Luego, una vez que tenga estos dos números, simplemente redondee al más cercano de los dos.

static int RoundToArray(int value, int[] array) { 
    int min = 0; 
    if (array[min] >= value) return array[min]; 

    int max = array.Length - 1; 
    if (array[max] <= value) return array[max]; 

    while (max - min > 1) { 
     int mid = (max + min)/2; 

     if (array[mid] == value) { 
      return array[mid]; 
     } else if (array[mid] < value) { 
      min = mid; 
     } else { 
      max = mid; 
     } 
    } 

    if (array[max] - value <= value - array[min]) { 
     return array[max]; 
    } else { 
     return array[min]; 
    } 

} 
+0

No solo supongo que, si se supone que esto funciona en colecciones grandes, una colección ordenada es el camino a seguir. –

+3

En la práctica, por supuesto, no importa, pero observo que está haciendo la forma "peligrosa" de encontrar el punto medio de dos enteros. Si min + max se desborda a negativo, entonces la mitad es un número negativo. Vea este artículo para un análisis entretenido del problema y sus posibles soluciones: http://locklessinc.com/articles/binary_search/ –

1

mediante LINQ:

int value = 17; 
var values = new float[] { 12, 15, 23, 94, 35, 48 }; 
if(value < values.First()) return value.First(); 
if(value > values.Last()) return value.Last(); 

float below = values.Where(v => v <= value).Max(); 
float above = values.Where(v => v >= value).Min(); 

if(value - below < above - value) 
    return below; 
else 
    return above; 

Mientras continuación, el número de valores posibles es bastante pequeña que esto debería funcionar. Si tiene miles de valores posibles, se debe usar otra solución, que tome ventaja de values ordenada (si realmente está ordenada).

+0

Error 'int' no contiene una definición para 'Primero' y ningún método de extensión 'Primero 'se puede encontrar la aceptación de un primer argumento de tipo' int '(¿falta una directiva using o una referencia de ensamblado?) – SKandeel

1

Se puede recorrer la matriz de números y establecer un roundedNum variable igual a cada uno si una variable delta es inferior a la corriente más baja delta. Algunas cosas se describen mejor en el código.

int roundedNum = myNum; 
int delta = myArray[myArray.Length-1] + 1; 

for(int i=0; i<myArray.Length; ++i) { 
    if(Math.Abs(myNum - myArray[i]) < delta) { 
     delta = Math.Abs(myNum - myArray[i]); 
     roundedNum = myArray[i]; 
    } 
} 

Eso debería hacer el truco bastante bien.

2

hacer algo como esto:

double distance = double.PositiveInfinity; 
float roundedValue = float.NaN; 
foreach (float f in list) 
{ 
    double d = Math.Abs(d - f); 
    if (d < distance) 
    { 
     distance = d; 
     roundedValue = f; 
    } 
} 
Cuestiones relacionadas