lower_bound == upper_bound

时间:2012-08-28 12:11:23

标签: c++ math stl naming-conventions

编辑:在文档中有太多的否定使我感到困惑。问题是我得到了相同的迭代器。我通过从lower_bound返回值中减去1来解决它。我用它进行插值:

    float operator()(double f)
        {
        SpectrumPoint* l=std::lower_bound(beginGet(),endGet(),(SpectrumPoint){float(f),0.0f}
            ,SpectrumPoint::CompareFreqLessThan);
        if(l>beginGet())
            {--l;}

        SpectrumPoint* u=std::lower_bound(beginGet(),endGet(),(SpectrumPoint){float(f),0.0f}
            ,SpectrumPoint::CompareFreqLessThan);

        if(u==endGet())
            {u=beginGet();}

        if(l==u)
            {
            if(u==endGet())
                {return u->amp;}
            return l->amp;
            }

        double f_min=l->freq;
        double A_min=l->amp;
        double f_max=u->freq;
        double A_max=u->amp;

        double delta_f=f_max-f_min;
        double delta_A=A_max-A_min;

        return A_min + delta_A*(f-f_min)/delta_f;
        }

我很抱歉这种混乱: - (

lower_bound是什么意思。如果我不得不猜测我会回答这个函数返回最后一个小于所要求值的元素的迭代器。但是我看到lower_bound与upper_bound几乎相同。唯一的区别是在upper_bound的情况下严格的不平等。在stl中是否存在与下界的正常定义一致的真正下界选择函数。

7 个答案:

答案 0 :(得分:96)

  • 下限:第一个大于或等于的元素。

  • 上限:第一个严格来说更大的元素。

示例:

+- lb(2) == ub(2)       +- lb(6)        +- lb(8)
|        == begin()     |  == ub(6)     |   +- ub(8) == end()
V                       V               V   V
+---+---+---+---+---+---+---+---+---+---+---+
| 3 | 4 | 4 | 4 | 4 | 5 | 7 | 7 | 7 | 7 | 8 |
+---+---+---+---+---+---+---+---+---+---+---+
    ^               ^                       ^
    |               |                       |
    +- lb(4)        +- ub(4)                +- lb(9) == ub(9) == end()

    |- eq-range(4) -|

如您所见, n 的半开等等范围是[lb( n ),ub( n ))

请注意,两个边界都为所需值的元素提供了有意义的插入位置,以便维持排序,但lower_bound具有如果元素已存在的区别特征,然后你得到一个实际指向该元素的迭代器。因此,您可以在订购范围内使用lower_bound来实现您自己的唯一会员多会员容器。

void insert(Container & c, T const & t)
{
    auto it = std::lower_bound(c.begin(), c.end(), t);

    // if unique container:
    if (it != c.end() && *it == t) { /* error, element exists! */ return; }

    c.insert(it, t);
}

答案 1 :(得分:8)

它将迭代器返回到最后一个小于所请求值的元素。这对于插入位置很有用(这就是函数返回迭代器的原因)。半开放范围first, lower_bound(first, last, value)指定小于value的所有值也很有用。

upper_bound将迭代器返回到最后一个元素[小于或等于/不大于]所要求的值。或者严格地说:值不小于的最后一个元素,因为两种算法都只在少于比较器的情况下处理。

如果在lower_bound返回的迭代器之前需要迭代器,则可以减去1(对于随机访问迭代器),减量(对于双向迭代器),或者进行线性搜索而不是使用{{1 (对于没有那些的前向迭代器)。

注意边缘情况,即没有任何元素小于所要求的值,在这种情况下,你不能拥有你想要的东西,因为它不存在。 lower_bound当然会返回范围的开头,因此不需要特殊情况的返回值。

答案 2 :(得分:6)

由于重新开放,我会尝试将我的评论作为答案。

名称lower_bound在数学上不正确。一个更好的名字可能是least_upper_bound,但这可能会混淆大多数非数学意识的人。 (然后你称之为upper_boundalmost_least_upper_bound?Yech!)

我的建议:克服名称lower_boundupper_bound在技术上不正确的事实。定义的两个函数非常有用。将这些功能视为对符号的有用滥用。

为了使数学上正确的lower_bound函数符合迭代器的C ++概念,函数必须返回反向迭代器而不是前向迭代器。返回反向迭代器并不像可能错误命名的lower_boundupper_bound采用的方法那样有用,并且返回反向迭代器的概念与所有容器都不可逆的事实相冲突。 / p>

为什么要使用反向迭代器?正如无法保证容器中存在上限一样,同样不能保证下限存在。现有的lower_boundupper_bound返回end(),表示搜索到的值超出了规模。真正的下限需要返回rend()以指示搜索到的值是否超出范围。

有一种方法可以以前向迭代器的形式实现真正的下界,但它的代价是滥用end()的含义来表示“没有下限”。这种滥用符号的问题在于该函数的某些用户可能会执行与true_lower_bound(off_scale_low_search_value)-1相当的操作并且瞧!一个指针指向集合中最大的元素。

那就是说,这是怎么做的。如果容器为空或者搜索的值小于容器中的第一个值,则使真正的下限函数返回end()。否则返回upper_bound()-1

答案 3 :(得分:2)

lower_boundupper_boundequal_range是按排序顺序执行二进制搜索的函数。对三个函数的需求来自于元素可以按顺序重复的事实:

1, 2, 3, 4, 4, 4, 5, 6, 7

在这种情况下,当搜索值4时,lower_bound将返回指向值为4的三个元素中的第一个的迭代器,upper_bound将返回指向具有值的元素的迭代器5,equal_range将返回包含这两个迭代器的对。

答案 4 :(得分:1)

奥赫!

您是否更改了原始代码,或者从第一天开始就出现了复制粘贴错误?

float operator()(double f)
{
    SpectrumPoint* l=std::lower_bound//...
...
    SpectrumPoint* u=std::lower_bound//...
...
}

在我今天阅读的代码中,您将low_bound分配给'l'和'u'。

答案 5 :(得分:1)

在大卫·哈门(David Hammen)回答之后,我试图解释为什么我们常常觉得lower_bound / upper_bound的名称不正确,或者至少不直观。

这是因为我们正在寻找一个比查询低的元素。 我做了一个图纸和一个用例:

sparse range queries

代码:

template< typename T, typename U >
auto infimum(std::map<T,U> const& ctr, T query)
{
    auto it = ctr.upper_bound(query);
    return it == ctr.begin() ? ctr.cend() : --it;
}

template< typename T, typename U >
bool is_in_interval(std::map<T,U> const& ctr, T query)
{
    auto inf = infimum(ctr, query);
    return inf == ctr.end() ? false : query <= inf->second;
}

https://ideone.com/jM8pt3

基本上要了解“灰色箭头”的行为,我们需要upper_bound - 1,这就是为什么它很奇怪的原因。

让我稍微改一下::我们本能地期望lower_bound这个名字returns-first-immediately-inferior-element(如灰色箭头)。但是我们得到returns-first-immediately-superior-element的lower_bound; first-immediately-strictly-superior-element表示upper_bound。真是令人惊讶。

假设您使用稀疏序列就像上图中的思想实验一样,这令人惊讶。但是,当以“ <{> equal_range的边界”以密集序列的形式想到它时,这是非常有意义的,其中充满了高原,如刻画精美的Kerrek SB。

测试代码:

#include <map>
#include <cassert>
#include <iostream>

// .. paste infimum and is_in_interval here

int main()
{
    using std::cout;
    using Map = std::map<int,int>;
    Map intervals{{2,5}, {8,9}};

    auto red = infimum(intervals, 4);
    assert(red->first == 2);
    cout << "red->first " << red->first << "\n";

    auto green = infimum(intervals, 6);
    assert(green->first == 2);
    cout << "green->first " << green->first << "\n";

    auto pink = infimum(intervals, 8);
    assert(pink->first == 8);
    cout << "pink->first " << pink->first << "\n";

    auto yellow = infimum(intervals, 1);
    assert(yellow == intervals.cend());

    auto larger_than_all = infimum(intervals, 15);
    assert(larger_than_all->first == 8);

    bool red_is = is_in_interval(intervals, 4);
    cout << "red is in " << red_is << "\n";

    bool green_is = is_in_interval(intervals, 6);
    cout << "green is in " << green_is << "\n";

    bool pink_is = is_in_interval(intervals, 8);
    cout << "pink is in " << pink_is << "\n";

    bool yellow_is = is_in_interval(intervals, 1);
    cout << "yellow is in " << yellow_is << "\n";
}

产生

red->first 2
green->first 2
pink->first 8
red is in 1
green is in 0
pink is in 1
yellow is in 0

似乎还可以。

因此,实用程序功能当然不是很好,应该使用范围API设计它们,因此我们可以使用任何集合或子范围,反向迭代器或筛选视图等。当我们拥有C ++ 20时,我们可以得到它。同时,我刚刚制作了一个简单的教育性地图获取API。

玩:
https://ideone.com/jM8pt3

答案 6 :(得分:0)

lower_boundupper_bound的另一种用法是在容器中找到一系列相等的元素,例如

std::vector<int> data = { 1, 1, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6 };

auto lower = std::lower_bound(data.begin(), data.end(), 4);
auto upper = std::upper_bound(lower, data.end(), 4);

std::copy(lower, upper, std::ostream_iterator<int>(std::cout, " "));