Respuestas:
Versión ES5:
var counts = [4, 9, 15, 6, 2],
goal = 5;
var closest = counts.reduce(function(prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
console.log(closest);
goal
para reducir, debe hacer referencia a él desde un ámbito global.
Aquí está el pseudocódigo que debe ser convertible a cualquier lenguaje de procedimiento:
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)
def closest (num, arr):
curr = arr[0]
foreach val in arr:
if abs (num - val) < abs (num - curr):
curr = val
return curr
Simplemente resuelve las diferencias absolutas entre el número dado y cada elemento de la matriz y le devuelve uno de los que tiene la mínima diferencia.
Para los valores de ejemplo:
number = 112 112 112 112 112 112 112 112 112 112
array = 2 42 82 122 162 202 242 282 322 362
diff = 110 70 30 10 50 90 130 170 210 250
|
+-- one with minimal absolute difference.
Como prueba de concepto, aquí está el código de Python que usé para mostrar esto en acción:
def closest (num, arr):
curr = arr[0]
for index in range (len (arr)):
if abs (num - arr[index]) < abs (num - curr):
curr = arr[index]
return curr
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
number = 112
print closest (number, array)
Y, si realmente lo necesita en Javascript, consulte a continuación un archivo HTML completo que demuestra la función en acción:
<html>
<head></head>
<body>
<script language="javascript">
function closest (num, arr) {
var curr = arr[0];
var diff = Math.abs (num - curr);
for (var val = 0; val < arr.length; val++) {
var newdiff = Math.abs (num - arr[val]);
if (newdiff < diff) {
diff = newdiff;
curr = arr[val];
}
}
return curr;
}
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
number = 112;
alert (closest (number, array));
</script>
</body>
</html>
Ahora tenga en cuenta que puede haber margen para mejorar la eficiencia si, por ejemplo, sus elementos de datos están ordenados (eso podría inferirse de los datos de la muestra pero no lo declara explícitamente). Podría, por ejemplo, utilizar una búsqueda binaria para encontrar el elemento más cercano.
También debe tener en cuenta que, a menos que necesite hacerlo muchas veces por segundo, las mejoras de eficiencia serán principalmente imperceptibles a menos que sus conjuntos de datos sean mucho más grandes.
Si no desea intentarlo de esa manera (y puede garantizar la matriz se ordena en orden ascendente), este es un buen punto de partida:
<html>
<head></head>
<body>
<script language="javascript">
function closest (num, arr) {
var mid;
var lo = 0;
var hi = arr.length - 1;
while (hi - lo > 1) {
mid = Math.floor ((lo + hi) / 2);
if (arr[mid] < num) {
lo = mid;
} else {
hi = mid;
}
}
if (num - arr[lo] <= arr[hi] - num) {
return arr[lo];
}
return arr[hi];
}
array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
number = 112;
alert (closest (number, array));
</script>
</body>
</html>
Básicamente utiliza el uso de corchetes y la comprobación del valor medio para reducir el espacio de la solución a la mitad para cada iteración, un O(log N)
algoritmo clásico , mientras que la búsqueda secuencial anterior fue O(N)
:
0 1 2 3 4 5 6 7 8 9 <- indexes
2 42 82 122 162 202 242 282 322 362 <- values
L M H L=0, H=9, M=4, 162 higher, H<-M
L M H L=0, H=4, M=2, 82 lower/equal, L<-M
L M H L=2, H=4, M=3, 122 higher, H<-M
L H L=2, H=3, difference of 1 so exit
^
|
H (122-112=10) is closer than L (112-82=30) so choose H
Como se dijo, eso no debería hacer una gran diferencia para los conjuntos de datos pequeños o para cosas que no necesitan ser cegadoramente rápidas, pero es una opción que puede considerar.
Versión ES6 (2015):
const counts = [4, 9, 15, 6, 2];
const goal = 5;
const output = counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
console.log(output);
Para la reutilización, puede incluir una función de curry que admita marcadores de posición ( http://ramdajs.com/0.19.1/docs/#curry o https://lodash.com/docs#curry ). Esto le da mucha flexibilidad dependiendo de lo que necesita:
const getClosest = curry((counts, goal) => {
return counts
.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
const closestTo5 = getClosest(_, 5);
const closestTo = getClosest([4, 9, 15, 6, 2]);
Código de trabajo de la siguiente manera:
var array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
function closest(array, num) {
var i = 0;
var minDiff = 1000;
var ans;
for (i in array) {
var m = Math.abs(num - array[i]);
if (m < minDiff) {
minDiff = m;
ans = array[i];
}
}
return ans;
}
console.log(closest(array, 88));
Si bien se publicaron algunas buenas soluciones aquí, JavaScript es un lenguaje flexible que nos brinda herramientas para resolver un problema de muchas maneras diferentes. Todo se reduce a tu estilo, por supuesto. Si su código es más funcional, encontrará la variación de reducción adecuada, es decir:
arr.reduce(function (prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
Sin embargo, algunos pueden encontrar eso difícil de leer, dependiendo de su estilo de codificación. Por lo tanto, propongo una nueva forma de resolver el problema:
var findClosest = function (x, arr) {
var indexArr = arr.map(function(k) { return Math.abs(k - x) })
var min = Math.min.apply(Math, indexArr)
return arr[indexArr.indexOf(min)]
}
findClosest(80, [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]) // Outputs 82
Al contrario de otros enfoques para encontrar el valor mínimo utilizando Math.min.apply
, éste no requiere de la matriz de entrada arr
a clasificar . No necesitamos preocuparnos por los índices ni ordenarlos de antemano.
Explicaré el código línea por línea para mayor claridad:
arr.map(function(k) { return Math.abs(k - x) })
Crea una nueva matriz, esencialmente almacenando los valores absolutos de los números dados (número en arr
) menos el número de entrada ( x
). Luego buscaremos el número más pequeño (que también es el más cercano al número de entrada)Math.min.apply(Math, indexArr)
Esta es una forma legítima de encontrar el número más pequeño en la matriz que acabamos de crear (nada más)arr[indexArr.indexOf(min)]
Esta es quizás la parte más interesante. Hemos encontrado nuestro número más pequeño, pero no estamos seguros de si debemos sumar o restar el número inicial ( x
). Eso es porque solíamos Math.abs()
encontrar la diferencia. Sin embargo, array.map
crea (lógicamente) un mapa de la matriz de entrada, manteniendo los índices en el mismo lugar. Por lo tanto, para encontrar el número más cercano, solo devolvemos el índice del mínimo encontrado en la matriz dada indexArr.indexOf(min)
.He creado un contenedor que lo demuestra.
3n
y es ES5 a pesar de que respondiste en 2016 y otras soluciones están bien, aunque este novato que hizo esta pregunta claramente no era un programador en ese momento.
O(n)
solución realiza alrededor de 100k operaciones / seg menos que @paxdiablo O(log n)
en números aleatorios. Cuando se diseña un algoritmo siempre se ordena primero, dicen. (Excepto si sabes lo que estás haciendo y tienes puntos de referencia para respaldarte.)
const findClosest = goal => (a,b) => Math.abs(a - goal) < Math.abs(b - goal) ? a : b;
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362].reduce(findClosest(80))
Todas las respuestas hasta ahora se concentran en buscar en toda la matriz. Teniendo en cuenta que su matriz ya está ordenada y realmente solo desea el número más cercano, esta es probablemente la solución más rápida:
var a = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
var target = 90000;
/**
* Returns the closest number from a sorted array.
**/
function closest(arr, target) {
if (!(arr) || arr.length == 0)
return null;
if (arr.length == 1)
return arr[0];
for (var i = 1; i < arr.length; i++) {
// As soon as a number bigger than target is found, return the previous or current
// number depending on which has smaller difference to the target.
if (arr[i] > target) {
var p = arr[i - 1];
var c = arr[i]
return Math.abs(p - target) < Math.abs(c - target) ? p : c;
}
}
// No number in array is bigger so return the last.
return arr[arr.length - 1];
}
// Trying it out
console.log(closest(a, target));
Tenga en cuenta que el algoritmo puede mejorarse enormemente, por ejemplo, utilizando un árbol binario.
a[i]
o i[0]
.
Todas las soluciones están sobredimensionadas.
Es tan simple como:
const needle = 5;
const haystack = [1, 2, 3, 4, 5, 6, 7, 8, 9];
haystack.sort((a, b) => {
return Math.abs(a - needle) - Math.abs(b - needle);
});
// 5
Esta solución utiliza el cuantificador existencial ES5 Array#some
, que permite detener la iteración, si se cumple una condición.
Opuesto a Array#reduce
, no necesita iterar todos los elementos para un resultado.
Dentro de la devolución de llamada, se toma un absoluto delta
entre el valor buscado y el real item
y se compara con el último delta. Si es mayor o igual, la iteración se detiene, porque todos los demás valores con sus deltas son mayores que el valor real.
Si la delta
devolución de llamada es menor, el elemento real se asigna al resultado y delta
se guarda en lastDelta
.
Finalmente, se toman valores más pequeños con deltas iguales, como en el siguiente ejemplo de 22
, que da como resultado 2
.
Si hay una prioridad de valores mayores, la verificación delta debe cambiarse de:
if (delta >= lastDelta) {
a:
if (delta > lastDelta) {
// ^^^ without equal sign
Esto obtendría con 22
el resultado 42
(Prioridad de valores mayores).
Esta función necesita valores ordenados en la matriz.
function closestValue(array, value) {
var result,
lastDelta;
array.some(function (item) {
var delta = Math.abs(value - item);
if (delta >= lastDelta) {
return true;
}
result = item;
lastDelta = delta;
});
return result;
}
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log(21, closestValue(data, 21)); // 2
console.log(22, closestValue(data, 22)); // 2 smaller value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82
function closestValue(array, value) {
var result,
lastDelta;
array.some(function (item) {
var delta = Math.abs(value - item);
if (delta > lastDelta) {
return true;
}
result = item;
lastDelta = delta;
});
return result;
}
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log(21, closestValue(data, 21)); // 2
console.log(22, closestValue(data, 22)); // 42 greater value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82
closestValue([ 2, 2, 42, 80 ], 50) === 2
ES6
/**
* Finds the nearest value in an array of numbers.
* Example: nearestValue(array, 42)
*
* @param {Array<number>} arr
* @param {number} val the ideal value for which the nearest or equal should be found
*/
const nearestValue = (arr, val) => arr.reduce((p, n) => (Math.abs(p) > Math.abs(n - val) ? n - val : p), Infinity) + val
Ejemplos:
let values = [1,2,3,4,5]
console.log(nearestValue(values, 10)) // --> 5
console.log(nearestValue(values, 0)) // --> 1
console.log(nearestValue(values, 2.5)) // --> 2
values = [100,5,90,56]
console.log(nearestValue(values, 42)) // --> 56
values = ['100','5','90','56']
console.log(nearestValue(values, 42)) // --> 56
No sé si se supone que debo responder una pregunta anterior, pero como esta publicación aparece primero en las búsquedas de Google, esperaba que me perdonara agregar mi solución y mi 2c aquí.
Siendo perezoso, no podía creer que la solución para esta pregunta fuera un LOOP, así que busqué un poco más y regresé con la función de filtro :
var myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
var myValue = 80;
function BiggerThan(inArray) {
return inArray > myValue;
}
var arrBiggerElements = myArray.filter(BiggerThan);
var nextElement = Math.min.apply(null, arrBiggerElements);
alert(nextElement);
Eso es todo !
goog.math.clamp
(cierre de google) solo con matrices y sin preocuparse por el límite inferior.
Mi respuesta a una pregunta similar también es responsable de los vínculos y está en Javascript simple, aunque no utiliza la búsqueda binaria, por lo que es O (N) y no O (logN):
var searchArray= [0, 30, 60, 90];
var element= 33;
function findClosest(array,elem){
var minDelta = null;
var minIndex = null;
for (var i = 0 ; i<array.length; i++){
var delta = Math.abs(array[i]-elem);
if (minDelta == null || delta < minDelta){
minDelta = delta;
minIndex = i;
}
//if it is a tie return an array of both values
else if (delta == minDelta) {
return [array[minIndex],array[i]];
}//if it has already found the closest value
else {
return array[i-1];
}
}
return array[minIndex];
}
var closest = findClosest(searchArray,element);
Me gusta el enfoque de Fusion, pero hay un pequeño error. Así es correcto:
function closest(array, number) {
var num = 0;
for (var i = array.length - 1; i >= 0; i--) {
if(Math.abs(number - array[i]) < Math.abs(number - array[num])){
num = i;
}
}
return array[num];
}
También es un poco más rápido porque utiliza el mejorado for
bucle .
Al final escribí mi función así:
var getClosest = function(number, array) {
var current = array[0];
var difference = Math.abs(number - current);
var index = array.length;
while (index--) {
var newDifference = Math.abs(number - array[index]);
if (newDifference < difference) {
difference = newDifference;
current = array[index];
}
}
return current;
};
Lo probé console.time()
y es un poco más rápido que la otra función.
improved for loop
? Los bucles invertidos no siempre son una mejora del rendimiento.
.length
solo una vez, cuando declara i
, mientras que para este ciclo. Pero creo var i = arr.length;while (i--) {}
que sería aún más rápido
while
. Ahora es aún más rápido.
Para un rango pequeño, lo más simple es tener una matriz de mapas, donde, por ejemplo, la 80a entrada tendría el valor 82, para usar su ejemplo. Para un rango mucho más amplio y escaso, probablemente el camino a seguir es una búsqueda binaria.
Con un lenguaje de consulta, puede buscar valores a cierta distancia a cada lado de su número de entrada y luego ordenar a través de la lista reducida resultante. Pero SQL no tiene un buen concepto de "siguiente" o "anterior", para darle una solución "limpia".
Otra variante aquí tenemos un rango circular que conecta la cabeza con los pies y acepta solo el valor mínimo para la entrada dada. Esto me ayudó a obtener valores de código de caracteres para uno de los algoritmos de cifrado.
function closestNumberInCircularRange(codes, charCode) {
return codes.reduce((p_code, c_code)=>{
if(((Math.abs(p_code-charCode) > Math.abs(c_code-charCode)) || p_code > charCode) && c_code < charCode){
return c_code;
}else if(p_code < charCode){
return p_code;
}else if(p_code > charCode && c_code > charCode){
return Math.max.apply(Math, [p_code, c_code]);
}
return p_code;
});
}
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
class CompareFunctor
{
public:
CompareFunctor(int n) { _n = n; }
bool operator()(int & val1, int & val2)
{
int diff1 = abs(val1 - _n);
int diff2 = abs(val2 - _n);
return (diff1 < diff2);
}
private:
int _n;
};
int Find_Closest_Value(int nums[], int size, int n)
{
CompareFunctor cf(n);
int cn = *min_element(nums, nums + size, cf);
return cn;
}
int main()
{
int nums[] = { 2, 42, 82, 122, 162, 202, 242, 282, 322, 362 };
int size = sizeof(nums) / sizeof(int);
int n = 80;
int cn = Find_Closest_Value(nums, size, n);
cout << "\nClosest value = " << cn << endl;
cin.get();
}
Lo más eficiente sería una búsqueda binaria. Sin embargo, incluso las soluciones simples pueden salir cuando el siguiente número coincide más con el actual . Casi todas las soluciones aquí no tienen en cuenta que la matriz está ordenada e iterando a pesar de todo: /
const closest = (orderedArray, value, valueGetter = item => item) =>
orderedArray.find((item, i) =>
i === orderedArray.length - 1 ||
Math.abs(value - valueGetter(item)) < Math.abs(value - valueGetter(orderedArray[i + 1])));
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log('21 -> 2', closest(data, 21) === 2);
console.log('22 -> 42', closest(data, 22) === 42); // equidistant between 2 and 42, select highest
console.log('23 -> 42', closest(data, 23) === 42);
console.log('80 -> 82', closest(data, 80) === 82);
Esto también se puede ejecutar en no primitivos, por ejemplo closest(data, 21, item => item.age)
Cambie find
a findIndex
para devolver el índice en la matriz.
Para encontrar dos números más cercanos en la matriz
function findTwoClosest(givenList, goal) {
var first;
var second;
var finalCollection = [givenList[0], givenList[1]];
givenList.forEach((item, firtIndex) => {
first = item;
for (let i = firtIndex + 1; i < givenList.length; i++) {
second = givenList[i];
if (first + second < goal) {
if (first + second > finalCollection[0] + finalCollection[1]) {
finalCollection = [first, second];
}
}
}
});
return finalCollection;
}
var counts = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
var goal = 80;
console.log(findTwoClosest(counts, goal));
Aquí está el fragmento de Código para encontrar el elemento más cercano a un número de una matriz en Complejidad O (nlog (n)): -
Entrada: - {1,60,0, -10,100,87,56} Elemento: - 56 Número más cercano en la matriz: - 60
Código fuente (Java):
package com.algo.closestnumberinarray;
import java.util.TreeMap;
public class Find_Closest_Number_In_Array {
public static void main(String arsg[]) {
int array[] = { 1, 60, 0, -10, 100, 87, 69 };
int number = 56;
int num = getClosestNumber(array, number);
System.out.println("Number is=" + num);
}
public static int getClosestNumber(int[] array, int number) {
int diff[] = new int[array.length];
TreeMap<Integer, Integer> keyVal = new TreeMap<Integer, Integer>();
for (int i = 0; i < array.length; i++) {
if (array[i] > number) {
diff[i] = array[i] - number;
keyVal.put(diff[i], array[i]);
} else {
diff[i] = number - array[i];
keyVal.put(diff[i], array[i]);
}
}
int closestKey = keyVal.firstKey();
int closestVal = keyVal.get(closestKey);
return closestVal;
}
}
x
, ir a través de la matriz una por una, comparari
con el número actual en la matriz, si la diferencia entre ella yi
es menor que el valor actual enx
, establecerx
el número de matriz actual. Cuando finaliza,x
tiene el número más cercano ai
la matriz.