简体   繁体   English

为索引寻找最接近的真实值

[英]Finding the closest true value for an index

Is there an elegant STL way to find the closest true (1) value in an array for a given index.是否有一种优雅的 STL 方法可以在给定索引的数组中找到最接近的true (1) 值。 For example, for std::vector<int> v{1,1,1,0,0,0,1,1,0};例如,对于std::vector<int> v{1,1,1,0,0,0,1,1,0}; the closest true value for a given index 5 is at index 6.给定索引 5 的最接近true值位于索引 6。

I tried and ended up using multiple while loops with iterators.我尝试并最终使用了多个带有迭代器的 while 循环。 Is it possible using C++ STL?是否可以使用 C++ STL?

Here's an idea that you can expand on to cover all your use-cases.这是一个您可以扩展以涵盖所有用例的想法。

You can use std::find and std::distance to achieve that.您可以使用std::findstd::distance来实现这一点。

Example ( live ):示例(现场):

#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>

int main()
{
    const std::vector<int> v { 1, 1, 1, 0, 0, 0, 1, 1, 0 };

    const auto it = std::find( std::begin(v) + 5, std::end(v), 1 );
    if ( it == std::end(v) )
    {
        std::cerr << "ERROR: Not found!\n";
        return EXIT_FAILURE;
    }

    const auto index = std::distance( std::begin(v), it );
    std::cout << "SUCCESS: Found at index " << index << '\n';

    return EXIT_SUCCESS;
}

You can wrap your own function with your default (ie true ) for better readability according to your use-case.您可以根据您的用例使用默认值(即true )包装您自己的函数以获得更好的可读性。 Also, look at std::next and std::advance for iterator navigation.另外,查看std::nextstd::advance以进行迭代器导航。


Another example using basic constructs only including some tests ( live ):另一个使用基本结构的例子只包括一些测试( live ):

#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>
#include <limits>

using Result = std::tuple<bool, std::size_t, std::string>; // result (T/F), index, message
Result find_nearest( const std::vector<int>& v, const std::size_t index, const int value )
{
    constexpr auto max_index = std::numeric_limits<std::size_t>::max();

    if ( v.empty() || index >= v.size() )
    {
        return { false, max_index, "Empty container or invalid index!" };
    }

    std::size_t ilhs { max_index };
    std::size_t irhs { max_index };

    for ( std::size_t i {0}; i != v.size(); ++i )
    {
        if ( v[i] == value )
        {
            if ( i < index ) { ilhs = i; }
            else if ( i > index ) { irhs = i; break; }
        }
    }

    // if element not found i.e. no index
    if ( ilhs == max_index && irhs == max_index )
    {
        return { false, max_index, "Index not found!" };
    }

    // shortest distance based comparison to determine indexes
    const auto dlhs = ( ilhs != max_index ? index - ilhs : ilhs );
    const auto drhs = ( irhs != max_index ? irhs - index : irhs );
    if ( dlhs == drhs )
    {
        return { true, ilhs, "Equal distance found! Left index returned!" };
    }

    const auto idx = ( dlhs < drhs ? ilhs : irhs );
    return { true, idx, "Index found!" };
}

int main()
{
    using Args  = std::tuple<std::vector<int>, std::size_t, int>; // list, index, value
    using Tests = std::vector<Args>;

    const Tests tests
    {
        { {}, 0, 1 },
        { { 1 }, 0, 1  },
        { { 1 }, 1, 1  },
        { { 1 }, 2, 1  },
        { { 0, 0, 0 }, 1, 1 },
        { { 0, 0, 0, 1, 0 }, 2, 1 },
        { { 1, 0, 0, 0, 1 }, 2, 1 },
        { { 1, 0, 0, 1, 0, 1, 0 }, 3, 1 },
        { { 1, 0, 0, 0, 1, 0, 0, 0, 1 }, 5, 1 },
    };

    for ( const auto& [list, index, value] : tests )
    {
        const auto& [found, idx, msg] = find_nearest( list, index, value );
        if ( found )
            std::cout << "INF: " << msg << " index: " << idx << '\n';
        else
            std::cerr << "ERR: " << msg << '\n';
    }

    return 0;
}

Output:输出:

ERR: Empty container or invalid index!
ERR: Index not found!
ERR: Empty container or invalid index!
ERR: Empty container or invalid index!
ERR: Index not found!
INF: Index found! index: 3
INF: Equal distance found! Left index returned! index: 0
INF: Index found! index: 5
INF: Index found! index: 4

This is the most concise version I could think of.这是我能想到的最简洁的版本。

  • Use cbegin to find from left->right, and crbegin() from right->left.使用cbegin从左->右查找,使用cbegin crbegin()从右->左查找。 But notice, there need to be some calculation to get the correct starting position in the latter circumstance.但请注意,在后一种情况下需要进行一些计算才能获得正确的起始位置。

You will need C++17 to support if init statement .您将需要C++17来支持if init 语句

#include <vector>
#include <iostream>
#include <algorithm>
#include <numeric>
int main()
{
    const std::vector<int> v{ 1, 1, 1, 0, 0, 0, 1, 1, 0 };

    const int index_to_find = 5;

    int rdistance = std::numeric_limits<int>::max();
    if (auto iter_right = std::find(std::cbegin(v) + index_to_find + 1, std::cend(v), 1); iter_right != std::cend(v))
        rdistance = std::distance(std::cbegin(v) + index_to_find, iter_right);

    int ldistance = std::numeric_limits<int>::max();
    if (auto iter_left = std::find(std::crbegin(v) + v.size() - index_to_find, std::crend(v), 1); iter_left != std::crend(v))
        ldistance = std::distance(std::crbegin(v) + v.size() - index_to_find - 1, iter_left);

    if (ldistance == std::numeric_limits<int>::max() && rdistance == std::numeric_limits<int>::max())
        std::cout << "Not found!\n";
    else
    {
        if (ldistance == rdistance)
            std::cout << "Found at index: " << index_to_find + ldistance << " and " << index_to_find - ldistance << "\n";
        else
            std::cout << "Found at index: " << (rdistance > ldistance ? index_to_find - ldistance : index_to_find + rdistance) << "\n";
    }
}

I believe the most STL's and C++-ish way of solving this problem is by using the combination of std::find , std::distance and, forward and reverse iterators:我相信解决这个问题的最 STL 和 C++ 风格的方法是使用std::findstd::distance和正向和反向迭代器的组合:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

int find_closest(std::vector<int> const& v, int i) {
    auto it = v.begin() + i;
    auto left_truth  = std::find(std::make_reverse_iterator(it), v.rend(), 1);
    auto right_truth = std::find(it + 1, v.end(), 1);

    auto left_distance  = std::distance(std::make_reverse_iterator(it), left_truth) + 1;
    auto right_distance = std::distance(it, right_truth);

    auto left_truth_index  = std::distance(v.begin(), --(left_truth.base()));
    auto right_truth_index = std::distance(v.begin(), right_truth);

    if (v.rend() != left_truth && v.end() != right_truth) {
        if (left_distance < right_distance) {
            return left_truth_index;
        } else {
            return right_truth_index;
        }
    } else if (v.end() == right_truth) {
        return left_truth_index;
    } else {
        return right_truth_index;
    }
}

You may notice that std::make_reverse_iterator is part of C++17 but, if you need to use C++11, you can easily make the same helper function:您可能会注意到std::make_reverse_iterator是 C++17 的一部分,但是,如果您需要使用 C++11,您可以轻松地创建相同的辅助函数:

template <typename Iter>
constexpr std::reverse_iterator<Iter> make_reverse_iterator(Iter i) {
    return std::reverse_iterator<Iter>(i);
}

Now, you can use the find_closest function like this:现在,您可以像这样使用find_closest函数:

int main() {
    std::vector<int> v {1, 1, 1, 0, 0, 0, 1, 1, 0};

    std::cout << find_closest(v, -6) << std::endl; // 0
    std::cout << find_closest(v, -1) << std::endl; // 0
    std::cout << find_closest(v, 0) << std::endl;  // 1
    std::cout << find_closest(v, 3) << std::endl;  // 2
    std::cout << find_closest(v, 5) << std::endl;  // 6
    std::cout << find_closest(v, 8) << std::endl;  // 7
    std::cout << find_closest(v, 10) << std::endl; // 7

    return 0;
}

Check it out live 现场查看

This is such a simple routine that you're best of writing a function from scratch.这是一个如此简单的例程,您最好从头开始编写函数。 Just search in both directions until you hit an end on any side, and then search in just one direction.只需向两个方向搜索,直到您在任何一侧到达终点,然后只向一个方向搜索。

#include <vector>
#include <iterator>
#include <iostream> 

template <typename IterT, typename ValueT>
IterT find_closest(IterT begin, IterT mid, IterT end, const ValueT& value)
{
    if (*mid == value) return mid;

    IterT lhs = mid;
    IterT rhs = std::next(mid);
    while(lhs != begin && rhs != end)
    {
        --lhs;
        if (*lhs == value) return lhs;
        if (*rhs == value) return rhs;
        ++rhs;
    }

    while(lhs != begin)
    {
        --lhs;
        if (*lhs == value) return lhs;
    }

    while(rhs != end)
    {
        if (*rhs == value) return rhs;
        ++rhs;
    }

    return end;
}

int main()
{
    std::vector<int> v{1,1,1,0,0,0,1,1,0};
    std::cout << &(*find_closest(v.begin(), v.begin() + 0, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 1, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 2, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 3, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 4, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 5, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 6, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 7, v.end(), 1)) - &(v[0]) << '\n';
    std::cout << &(*find_closest(v.begin(), v.begin() + 8, v.end(), 1)) - &(v[0]) << '\n';
}

mid must be dereferencable mid必须是可取消引用的

demo: http://coliru.stacked-crooked.com/a/66d18019fb7060f6演示: http : //coliru.stacked-crooked.com/a/66d18019fb7060f6

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM