web-dev-qa-db-fra.com

Manière élégante de trouver la valeur la plus proche dans un vecteur d'en haut

J'ai besoin d'une fonction qui prend un vecteur (supposé être trié), et une valeur, et retourne le nombre le plus proche qui soit [edit] plus grand que inférieur ou égal à ce nombre, de préférence en utilisant un algorithme de la STL. Je suis venu avec une solution en utilisant std :: lower_bound (), mais il semble kludgy et laid:

struct ClosestCmp {
    bool operator()(const int & x, const int & y) { return x > y; }
};

// vec is assumed to be sorted
int closest(const std::vector<int> & vec, int value)
{
    std::vector<int>::const_reverse_iterator cri =
        std::lower_bound(vec.rbegin(), vec.rend(), value, ClosestCmp());
    if (cri != vec.rend()) {
        return *cri;
    }
    return -1;
}

// ...
vec.Push_back(1);
vec.Push_back(2);
vec.Push_back(4);
vec.Push_back(5);
std::cout << closest(vec, 2) << "\n"; // Should ouput "2"
std::cout << closest(vec, 3) << "\n"; // Should ouput "2"
std::cout << closest(vec, 4) << "\n"; // Should ouput "4"

Quelqu'un peut-il suggérer un moyen plus élégant, peut-être d'utiliser un algorithme STL sans avoir besoin d'une fonction de comparaison ou d'un itérateur inversé? J'ai regardé dans la STL, mais n'ai pas été en mesure de trouver une meilleure solution que celle-là.

18
josmith42

Vous ne pouvez utiliser que std::lower_bound et std::upper_bound avec des prédicats binaires correspondant à l'ordre du conteneur. Donc, vous ne pouvez pas trier par < et ensuite utiliser un prédicat binaire différent (disons <= ou >). Donc, votre "kludge" est en fait la bonne chose à faire. Le vecteur trié inversé correspond aux critères de classement que vous souhaitez utiliser pour rechercher l'élément inférieur ou égal à la valeur. (Sinon, si vous recherchiez la valeur supérieure ou égale à, vous pouvez simplement utiliser std::lower_bound.)

5
MSN

Pour rappel:

  • std::lower_bound: renvoie la première valeur qui ne compare pas moins
  • std::upper_bound: renvoie la première valeur comparant strictement plus grande

De votre description, std::lower_bound ressemble déjà à l'ajustement parfait, ce qui ne va pas avec:

int closest(std::vector<int> const& vec, int value) {
    auto const it = std::lower_bound(vec.begin(), vec.end(), value);
    if (it == vec.end()) { return -1; }

    return *it;
}

Qui est utilisé comme:

int main() {
    std::vector<int> vec;
    vec.Push_back(2);
    vec.Push_back(4);

    std::cout << closest(vec, 2) << "\n";
    std::cout << closest(vec, 3) << "\n";
    std::cout << closest(vec, 4) << "\n";
}

Sortie:

2
4
4
16
Matthieu M.

Quelque chose comme cela fonctionnerait ... prend la valeur la plus petite possible:

Pourrait être fabriqué comme un modèle ou quelque chose à la place pour ceux qui comprennent la programmation par modèles. http://ideone.com/ff46ax

#include <iostream>
#include <vector>
#include <map>
#include <stdlib.h>

int main()
{
    int comparevalue = 3;
    typedef std::vector<int> intvec;
    intvec myvec;

    myvec.Push_back(1);
    myvec.Push_back(2);
    myvec.Push_back(4);
    myvec.Push_back(5);
    myvec.Push_back(6);
    myvec.Push_back(7);

    typedef std::map<int, int> intmap;
    intmap mymap;

    for (intvec::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr)
        mymap.insert(std::make_pair(abs(*itr-comparevalue), *itr));

    std::cout << "difference:" << mymap.begin()->first << "\n";
    std::cout << "value:" << mymap.begin()->second;
    return 0;
}
2
LilleCarl

Nécessite C++ 11:

template<typename InputIterator, typename ValueType>
InputIterator closest(InputIterator first, InputIterator last, ValueType value)
{
    return std::min_element(first, last, [&](ValueType x, ValueType y)
    {   
        return std::abs(x - value) < std::abs(y - value);
    });
}
2
Robert

Pour le plus grand qui est inférieur ou égal, on peut utiliser cette fonction

int closest(std::vector<int> const& vec, int value) {
    auto const it = std::lower_bound(vec.begin(), vec.end(), value);
    if (it == vec.begin()) { return -1; }
    else return *(it - 1);
}
0
Alexandr Priymak