Skip to content

Commit

Permalink
[numeric] minMaxMeanMedian speed improvement openMVG#1398
Browse files Browse the repository at this point in the history
- Add unit test
- Use nth_element and min/max_element to minimize the number of
operation

A faster solution could be to use somthing like the following:
```
T min = a[0], max = a[0];
std::nth_element(a.begin(), a.begin() + n, a.end(),
    [&](T lhs, T rhs) {
        min = std::min(min, std::min(lhs, rhs));
        max = std::max(max, std::max(lhs, rhs));
        return lhs < rhs;
    });
```
but since nth_element is not guaranted to look to all element in the
array I prefer rely on 3 STL operation.
  • Loading branch information
pmoulon committed Jan 5, 2020
1 parent fb397b3 commit e41d6cd
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 6 deletions.
14 changes: 8 additions & 6 deletions src/openMVG/numeric/numeric.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,16 +370,18 @@ bool minMaxMeanMedian( DataInputIterator begin, DataInputIterator end,
}

std::vector<Type> vec_val( begin, end );
std::sort( vec_val.begin(), vec_val.end() );
min = vec_val[0];
max = vec_val[vec_val.size() - 1];
mean = std::accumulate( vec_val.begin(), vec_val.end(), Type( 0 ) )

// Get the median value:
const auto middle = vec_val.begin() + vec_val.size() / 2;
std::nth_element(vec_val.begin(), middle, vec_val.end());
median = *middle;
min = *std::min_element(vec_val.begin(), middle);
max = *std::max_element(middle, vec_val.end());
mean = std::accumulate( vec_val.cbegin(), vec_val.cend(), Type( 0 ) )
/ static_cast<Type>( vec_val.size() );
median = vec_val[vec_val.size() / 2];
return true;
}


/**
* @brief Display to standard output min, max, mean and median value of input range
* @param begin start of range
Expand Down
21 changes: 21 additions & 0 deletions src/openMVG/numeric/numeric_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "testing/testing.h"

#include <iostream>
#include <set>

using namespace openMVG;
using namespace std;
Expand Down Expand Up @@ -121,6 +122,26 @@ TEST(Numeric, MeanAndVarianceAlongRows) {
EXPECT_NEAR(1.25, variance(1), 1e-8);
}

TEST(Numeric, minMaxMeanMedian)
{
const int vec_size = 12;
std::vector<double> values(vec_size);
std::iota(values.begin(), values.end(), 0.0);
double min, max, mean, median;
minMaxMeanMedian(values.cbegin(), values.cend(), min, max, mean, median);

// For the GT value, use a set to sort the value
// and then collect the min, max, median and mean value
const std::set<double> set_values(values.cbegin(), values.cend());
EXPECT_NEAR(0.0, min, 1e-8);
EXPECT_NEAR(values.size() - 1, max, 1e-8);
auto set_begin_it = set_values.cbegin();
std::advance (set_begin_it, values.size() / 2);
EXPECT_NEAR(*set_begin_it, median, 1e-8);
const double mean_gt = std::accumulate(values.begin(), values.end(), 0.0) / static_cast<double>(values.size());
EXPECT_NEAR(mean_gt, mean, 1e-8);
}

/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
/* ************************************************************************* */

0 comments on commit e41d6cd

Please sign in to comment.