web-dev-qa-db-fra.com

obtenir le nombre le plus proche du tableau

J'ai un nombre de moins 1000 à plus 1000 et j'ai un tableau avec des nombres. Comme ça:

[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]

Je veux que le nombre que j'ai change au nombre le plus proche du tableau.

Par exemple, je reçois 80 en tant que nombre. Je souhaite qu’il reçoive 82.

112
noob

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

140
Joe Grund

Voici le pseudo-code qui devrait être convertible en n'importe quel langage procédural:

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

Il calcule simplement les différences absolues entre le nombre donné et chaque élément du tableau et vous renvoie l’un de ceux présentant la différence minimale.

Pour les valeurs d'exemple:

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.

En guise de preuve de concept, voici le code Python que j'ai utilisé pour le montrer en action:

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)

Et, si vous en avez vraiment besoin en Javascript, voyez ci-dessous un fichier HTML complet illustrant la fonction en action:

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

À présent, gardez à l’esprit que l’efficacité pourrait être améliorée si, par exemple, vos éléments de données étaient triés (ce qui pourrait être déduit des exemples de données, mais vous ne le déclarez pas explicitement). Vous pouvez, par exemple, utiliser une recherche binaire pour trouver l'élément le plus proche.

Vous devez également garder à l’esprit que, sauf si vous devez le faire plusieurs fois par seconde, les gains d’efficacité seront pour la plupart imperceptibles à moins que vos ensembles de données n’obtiennent beaucoup plus gros.

Si vous faites vous voulez l'essayer de cette façon (et pouvez garantir que le tableau est trié par ordre croissant), c'est un bon point de départ:

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

Il utilise essentiellement bracketing et vérifie la valeur du milieu pour réduire l’espace de la solution de moitié à chaque itération, un algorithme classique O(log N) alors que la recherche séquentielle ci-dessus était 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

Comme indiqué, cela ne devrait pas faire beaucoup de différence pour les petits ensembles de données ou pour des choses qui ne nécessitent pas une rapidité aveuglante, mais c'est une option vous voudrez peut-être envisager.

137
paxdiablo

ES6 (2015) Version:

const counts = [4, 9, 15, 6, 2];
const goal = 5;

counts
 .reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);

Pour la réutilisation, vous pouvez intégrer une fonction curry prenant en charge les espaces réservés ( http://ramdajs.com/0.19.1/docs/#curry ou https://lodash.com/docs#curry ) . Cela donne beaucoup de flexibilité en fonction de vos besoins:

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]);
28
Joe Grund

Code de travail comme ci-dessous:

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;
}
alert(closest(array,88));
20
Umesh Patil

Fonctionne avec des tableaux non triés

Bien que de bonnes solutions aient été publiées ici, JavaScript est un langage flexible qui nous donne des outils pour résoudre un problème de différentes manières ..__ Tout dépend de votre style, bien sûr. Si votre code est plus fonctionnel, vous constaterez que réduire les variations convient, c’est-à-dire:

  arr.reduce(function (prev, curr) {
    return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
  });

Cependant, certains peuvent trouver cela difficile à lire, en fonction de leur style de codage. Je propose donc une nouvelle façon de résoudre le problème:

  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

Contrairement à autres approches recherche de la valeur minimale à l'aide de Math.min.apply, celle-ci ne nécessite pas le tri du tableau d'entrée arr}. Nous n'avons pas besoin de nous soucier des index ni de les trier à l'avance.

Je vais expliquer le code ligne par ligne pour plus de clarté:

  1. arr.map(function(k) { return Math.abs(k - x) }) Crée un nouveau tableau, stockant essentiellement les valeurs absolues des nombres donnés (nombre dans arr) moins le nombre entré (x). Nous chercherons ensuite le plus petit nombre (qui est aussi le plus proche du nombre saisi)
  2. Math.min.apply(Math, indexArr) C'est un moyen légitime de trouver le plus petit nombre dans le tableau que nous venons de créer (rien de plus)
  3. arr[indexArr.indexOf(min)] C'est peut-être la partie la plus intéressante. Nous avons trouvé notre plus petit nombre, mais nous ne savons pas si nous devrions ajouter ou soustraire le nombre initial (x). C'est parce que nous avons utilisé Math.abs() pour trouver la différence. Cependant, array.map crée (logiquement) une carte du tableau en entrée, en conservant les index au même endroit. Par conséquent, pour trouver le nombre le plus proche, nous renvoyons simplement l'index du minimum trouvé dans le tableau donné indexArr.indexOf(min).

J'ai créé un bin pour le démontrer.

12
Dan Mindru

Pour les tableaux triés (recherche linéaire)

Jusqu'à présent, toutes les réponses se concentrent sur la recherche dans l'ensemble du tableau . Étant donné que votre tableau est déjà trié et que vous ne voulez que le nombre le plus proche, c'est probablement la solution la plus rapide:

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(arr, target) );

Notez que l’algorithme peut être considérablement amélioré, par exemple. en utilisant un arbre binaire.

10
Hubert Grzeskowiak

Il s'agit d'une proposition avec un quantificateur ES5 existentielArray#some , qui permet d'arrêter l'itération si une condition est remplie.

Contrairement à Array#reduce , il n’est pas nécessaire d’itérer tous les éléments pour obtenir un résultat.

À l'intérieur du rappel, un delta absolu entre la valeur recherchée et le item réel est pris et comparé au dernier delta. Si elle est supérieure ou égale, l'itération s'arrête car toutes les autres valeurs avec leurs deltas sont supérieures à la valeur réelle.

Si delta dans le rappel est plus petit, l'élément réel est affecté au résultat et delta est enregistré das lastDelta.

Dans le résultat, des valeurs plus petites avec des deltas égaux sont prises, comme dans l'exemple ci-dessous de 22, qui donne 2.

S'il existe une priorité de valeurs supérieures, la vérification du delta doit être modifiée de

if (delta >= lastDelta) {

à 

if (delta > lastDelta) {
//       ^^^ without equal sign

Cela donnerait avec 22, le résultat 42 (priorité des valeurs les plus élevées).

Cette fonction nécessite des valeurs triées dans le tableau.


Code avec priorité des valeurs plus petites:

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

Code avec priorité de plus grandes valeurs

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

4
Nina Scholz

Je ne sais pas si je suis censé répondre à une vieille question, mais comme ce message apparaît en premier sur les recherches Google, j'espère que vous me pardonnerez d'ajouter ma solution et mon 2c ici.

Étant paresseux, je ne pouvais pas croire que la solution à cette question serait une boucle. J'ai donc cherché un peu plus et suis revenu avec filter function :

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

C'est tout !

3
FrenchieFred

Ma réponse à une question similaire est la comptabilisation des liens aussi et il est en Javascript clair, bien qu'il n'utilise pas de recherche binaire, donc c'est O(N) et 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);

https://stackoverflow.com/a/26429528/986160

2

J'aime l'approche de Fusion, mais il y a une petite erreur. Comme ça c'est correct:

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

Il est également un peu plus rapide car il utilise la boucle améliorée for.

A la fin j'ai écrit ma fonction comme ceci:

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

Je l'ai testé avec console.time() et il est légèrement plus rapide que l'autre fonction.

2
Jon

Une recherche binaire légèrement modifiée sur le tableau fonctionnerait.

0
holygeek

Toutes les solutions sont sur-conçues.

C'est aussi simple que:

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
0
Gajus

Pour une petite plage, le plus simple est d’avoir un tableau de cartes, où, par exemple, la 80e entrée aurait la valeur 82, pour utiliser votre exemple. Pour une plage beaucoup plus large et clairsemée, le chemin à parcourir est probablement une recherche binaire.

Avec un langage d'interrogation, vous pouvez interroger des valeurs à une certaine distance de l'un ou l'autre côté de votre numéro d'entrée, puis trier la liste réduite résultante. Mais SQL n'a pas un bon concept de "suivant" ou "précédent", pour vous donner une solution "propre".

0
Hot Licks

Une autre variante consiste ici en une plage circulaire reliant la tête aux pieds et n’accepte que la valeur minimale d’une entrée donnée. Cela m'avait aidé à obtenir les valeurs de code de caractères pour l'un des algorithmes de chiffrement.

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;
  });
}
0
Vikas
#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();
}
0
Rushikesh