Risposte:
Versione 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
per ridurre, è necessario fare riferimento a un ambito globale.
Ecco lo pseudo-codice che dovrebbe essere convertibile in qualsiasi linguaggio procedurale:
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
Risolve semplicemente le differenze assolute tra il numero dato e ciascun elemento dell'array e ti restituisce uno di quelli con la differenza minima.
Per i valori di esempio:
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.
Come prova del concetto, ecco il codice Python che ho usato per mostrarlo in azione:
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)
E, se ne hai davvero bisogno in Javascript, vedi sotto per un file HTML completo che dimostra la funzione in azione:
<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>
Ora tieni presente che potrebbe esserci spazio per una maggiore efficienza se, ad esempio, i tuoi elementi di dati sono ordinati (che potrebbe essere dedotto dai dati di esempio ma non lo dichiari esplicitamente). Ad esempio, è possibile utilizzare una ricerca binaria per trovare l'elemento più vicino.
Dovresti anche tenere presente che, a meno che non sia necessario farlo più volte al secondo, i miglioramenti dell'efficienza saranno quasi impercettibili a meno che i tuoi set di dati non diventino molto più grandi.
Se non volete provare in questo modo (e in grado di garantire l'array è ordinato in ordine crescente), questo è un buon punto di partenza:
<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>
In pratica utilizza il bracketing e il controllo del valore medio per ridurre della metà lo spazio della soluzione per ogni iterazione, un O(log N)
algoritmo classico mentre la ricerca sequenziale sopra era 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
Come detto, ciò non dovrebbe fare molta differenza per piccoli set di dati o per cose che non devono essere accecanti, ma è un'opzione che potresti prendere in considerazione.
Versione 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);
Per la riusabilità è possibile includere una funzione curry che supporta segnaposto ( http://ramdajs.com/0.19.1/docs/#curry o https://lodash.com/docs#curry ). Ciò offre molta flessibilità a seconda delle necessità:
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]);
Codice di lavoro come di seguito:
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));
Mentre c'erano alcune buone soluzioni pubblicate qui, JavaScript è un linguaggio flessibile che ci offre strumenti per risolvere un problema in molti modi diversi. Dipende tutto dal tuo stile, ovviamente. Se il tuo codice è più funzionale, troverai la variazione di riduzione adatta, ovvero:
arr.reduce(function (prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
Tuttavia, alcuni potrebbero trovarlo difficile da leggere, a seconda del loro stile di codifica. Pertanto propongo un nuovo modo di risolvere il 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
Contrariamente ad altri approcci che trovano il valore minimo usando Math.min.apply
, questo non richiede che l'array di input arr
sia ordinato . Non dobbiamo preoccuparci degli indici o ordinarli in anticipo.
Spiegherò il codice riga per riga per chiarezza:
arr.map(function(k) { return Math.abs(k - x) })
Crea un nuovo array, essenzialmente memorizzando i valori assoluti dei numeri dati (numero in arr
) meno il numero di input ( x
). Quindi cercheremo il numero più piccolo (che è anche il più vicino al numero di input)Math.min.apply(Math, indexArr)
Questo è un modo legittimo per trovare il numero più piccolo nell'array che abbiamo appena creato prima (niente di più)arr[indexArr.indexOf(min)]
Questa è forse la parte più interessante. Abbiamo trovato il nostro numero più piccolo, ma non siamo sicuri se dovremmo aggiungere o sottrarre il numero iniziale ( x
). Questo perché abbiamo Math.abs()
trovato la differenza. Tuttavia, array.map
crea (logicamente) una mappa dell'array di input, mantenendo gli indici nello stesso posto. Pertanto, per scoprire il numero più vicino, restituiamo semplicemente l'indice del minimo trovato nell'array specificato indexArr.indexOf(min)
.Ho creato un cestino dimostrandolo.
3n
ed è ES5 anche se si risponde nel 2016 e altre soluzioni vanno bene anche se questo noob che ha posto questa domanda chiaramente non era un programmatore al momento.
O(n)
soluzione esegue circa 100k op / sec in meno rispetto O(log n)
a @paxdiablo su numeri casuali. Quando si progetta un algoritmo, ordinare sempre per primo, dicono. (Tranne se sai cosa stai facendo e hai dei benchmark per
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))
Tutte le risposte finora si concentrano sulla ricerca nell'intero array. Considerando che il tuo array è già ordinato e vuoi davvero solo il numero più vicino, questa è probabilmente la soluzione più veloce:
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));
Si noti che l'algoritmo può essere notevolmente migliorato, ad esempio utilizzando un albero binario.
a[i]
o i[0]
.
Tutte le soluzioni sono troppo ingegnerizzate.
È semplice come:
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
Questa soluzione utilizza il quantificatore esistenziale ES5 Array#some
, che consente di interrompere l'iterazione, se viene soddisfatta una condizione.
Al Array#reduce
contrario, non è necessario iterare tutti gli elementi per un risultato.
All'interno del callback, viene preso un assoluto delta
tra il valore cercato e l'effettivo item
e confrontato con l'ultimo delta. Se maggiore o uguale, l'iterazione si interrompe, poiché tutti gli altri valori con i loro delta sono maggiori del valore effettivo.
Se la voce delta
nel callback è più piccola, l'elemento reale viene assegnato al risultato e delta
viene salvato in lastDelta
.
Infine, vengono presi valori più piccoli con delta uguali, come nell'esempio seguente di 22
, che risulta in 2
.
Se esiste una priorità di valori maggiori, il controllo delta deve essere modificato da:
if (delta >= lastDelta) {
per:
if (delta > lastDelta) {
// ^^^ without equal sign
Ciò porterebbe con 22
il risultato 42
(Priorità di valori maggiori).
Questa funzione richiede valori ordinati nell'array.
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
Esempi:
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
Non so se dovrei rispondere a una vecchia domanda, ma dato che questo post appare prima nelle ricerche di Google, ho sperato che mi avresti perdonato aggiungendo la mia soluzione e il mio 2c qui.
Essendo pigro, non potevo credere che la soluzione per questa domanda sarebbe stata un LOOP, quindi ho cercato un po 'di più e sono tornato con la funzione 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);
È tutto !
goog.math.clamp
(chiusura di Google) solo con gli array e senza preoccuparti del limite inferiore.
La mia risposta a una domanda simile è tenere conto anche dei legami ed è in Javascript semplice, sebbene non usi la ricerca binaria, quindi è O (N) e non 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);
Mi piace l'approccio di Fusion, ma c'è un piccolo errore in esso. In questo modo è corretto:
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];
}
È anche un po 'più veloce perché utilizza il for
loop migliorato .
Alla fine ho scritto la mia funzione in questo modo:
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;
};
L'ho provato con console.time()
ed è leggermente più veloce dell'altra funzione.
improved for loop
? I loop invertiti non sono sempre un miglioramento delle prestazioni.
.length
una sola volta, quando dichiari i
, mentre per questo ciclo. Ma penso che var i = arr.length;while (i--) {}
sarebbe ancora più veloce
while
. Ora è ancora più veloce.
Una ricerca binaria leggermente modificata sull'array funzionerebbe.
Per un piccolo intervallo, la cosa più semplice è avere un array di mappe, dove, ad esempio, l'80a voce avrebbe il valore 82 in essa, per usare il tuo esempio. Per un intervallo molto più ampio e scarso, probabilmente la strada da percorrere è una ricerca binaria.
Con un linguaggio di query è possibile eseguire una query per valori a una certa distanza su entrambi i lati del numero di input e quindi ordinare l'elenco ridotto risultante. Ma SQL non ha un buon concetto di "prossimo" o "precedente", per darti una soluzione "pulita".
Un'altra variante qui abbiamo un intervallo circolare che collega la testa ai piedi e accetta solo il valore minimo per l'input dato. Ciò mi aveva aiutato a ottenere i valori del codice char per uno degli algoritmi di crittografia.
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();
}
La più efficiente sarebbe una ricerca binaria. Tuttavia, anche le soluzioni semplici possono uscire quando il numero successivo è un'ulteriore corrispondenza dalla corrente . Quasi tutte le soluzioni qui non tengono conto del fatto che l'array sia ordinato e iterando nel complesso: /
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);
Questo può essere eseguito anche su non primitivi, ad es closest(data, 21, item => item.age)
Passare find
a findIndex
per restituire l'indice nella matrice.
Per trovare il numero più vicino nell'array
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));
Ecco lo snippet di codice per trovare l'elemento più vicino a un numero di un array in Complexity O (nlog (n)): -
Input: - {1,60,0, -10.100,87,56} Elemento: - 56 Numero più vicino nell'array: - 60
Codice sorgente (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
, attraversa l'array uno per uno, confrontai
con il numero corrente nell'array, se la differenza tra esso edi
è inferiore al valore corrente inx
, impostatox
sul numero di array corrente. Al termine,x
ha il numero più vicinoi
all'array.