Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding the closest true value for an index

Tags:

c++

stl

Is there an elegant STL way to find the closest true (1) value in an array for a given index. For example, for 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.

I tried and ended up using multiple while loops with iterators. Is it possible using C++ STL?

like image 927
rohitt Avatar asked Feb 02 '20 04:02

rohitt


People also ask

How to find the closest value to the given value?

Excel Find Closest Value 1 Select the range where you will search for closest values to the give value, and then click Kutools > Select > Select... 2 In the opening Select Specific Cells dialog box, See More....

How to find the nearest or nearest number in an array?

Find the closest or nearest number with array formula. For example, you have a list of numbers in Column A, and now you will find the closest value or the nearest value of 18 from the Column A. You can do it as follows: Select a blank cell, and enter below formula, and press the Ctrl + Shift + Enter keys together.

How do I find the closest text to a specific string?

Find Text Closest to Text 1 (1) Check the Specified option, and select the range where you will look for closest text strings; 2 (2) Check the Find by specified text option; 3 (3) Go to the Text box, and type the specified text whose closest text strings you will find; 4 (4) In the Maximum number of different characters box,... See More....

How to find less than or equal to a given value?

In the opening Select Specific Cells dialog box, (1) Check the Cell option in the Selection type section; (2) In the Specific type section, click the first drop down list and select Greater than or equal to from it and type 16 into following box, and then select Less than or equal to from the second drop down list and type 20 into following box.


2 Answers

This is the most concise version I could think of.

  • Use cbegin to find from left->right, and crbegin() from right->left. 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.

#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";
    }
}
like image 95
sz ppeter Avatar answered Oct 03 '22 19:10

sz ppeter


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.

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 (i.e. true) for better readability according to your use-case. Also, look at std::next and std::advance for iterator navigation.


Another example using basic constructs only including some tests (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
like image 33
Azeem Avatar answered Oct 03 '22 18:10

Azeem