web-dev-qa-db-ja.com

上からベクトルの最も近い値を見つけるエレガントな方法

ベクトル(ソートされていると想定)と値を受け取り、最も近い数値を返す関数が必要です[編集] より大きい その数以下、できればSTLのアルゴリズムを使用。 std :: lower_bound()を使用した解決策を考え出しましたが、それは扱いにくく醜いようです:

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"

誰かがよりエレガントな方法を提案できますか?比較関数や逆反復子を必要とせずにSTLアルゴリズムを使用しているのでしょうか?私はSTLを調べましたが、これよりも良い解決策を見つけることができませんでした。

22
josmith42

std::lower_boundおよびstd::upper_boundは、コンテナーの順序と一致するバイナリ述語でのみ使用できます。したがって、<で並べ替えてから、別のバイナリ述語(<=または>など)を使用することはできません。したがって、「kludge」は実際には正しいことです。逆にソートされたベクトルは、値以下の要素を見つけるために使用する順序付け基準です。 (それ以外の場合は、実際に以上の値を検索する場合は、std::lower_boundを使用できます。)

5
MSN

注意:

  • std::lower_bound:比較しない最初の値を返します
  • std::upper_bound:厳密に比較する最初の値を返します

あなたの説明から、std::lower_boundはすでに完全に適合しているように見えますが、何が問題になっていますか。

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

これは次のように使用されます。

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

出力:

2
4
4
20
Matthieu M.

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);
    });
}
5
Robert

このような何かがうまくいくでしょう...最も小さい値をとります:

テンプレートプログラミングを理解している人のために、テンプレートまたは何かの代わりに多作にすることができます。 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

以下の最大のものについては、この関数を使用できます

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