Ваш вопрос работает, по крайней мере, два различных уровня: Вы спрашиваете, как реализовать определенный алгоритм идиоматический в C++ 11, и в то же время вы просите для эффективного алгоритма для вычислений среднее из списка целых чисел.
Вы правильно заметить, что для вычисления медианный, все, что мы должны сделать, это запустить QuickSelect algorithm с k
устанавливается равным n/2
. В стандартной библиотеке C++, пишутся Быстрый выбор std::nth_element
:
int v[] = { 5, 6, 4, 3, 2, 6, 7, 9, 3 };
const int k = std::size(v)/2;
std::nth_element(std::begin(v), &v[k], std::end(v)); // mutate in-place
int median = v[v.size()/2]; // now the k'th element is
(Для std::size
, см предложения N4280, coming soon to a C++17 near you! До тех пор, используйте ваш любимый NELEM макрос, или вернуться к использованию кучи выделено vector
.)
Эта реализация не Быстрый выбор действительно имеет ничего общего с «найти элемент массива х к таким образом, что ∑ я | х я − х к | сведено к минимуму ». Я имею в виду, это математически эквивалентно, да, но в коде нет, что соответствует суммированию или вычитанию целых чисел.
Наивный алгоритм "найти элемент массива х к таким образом, что ∑ я | х я − х к | минимизируется" просто
int v[] = { 5, 6, 4, 3, 2, 6, 7, 9, 3 };
auto sum_of_differences = [v](int xk) {
int result = 0;
for (auto&& xi : v) {
result += std::abs(xi - xk);
}
return result;
};
int median =
std::min_element(std::begin(v), std::end(v), [](int xa, int xb) {
return sum_of_differences(xa) < sum_of_differences(xb);
});
Это ужасно неэффективный алгоритм, gi что QuickSelect выполняет ту же работу. Однако, тривиально расширять этот код, чтобы работать с любой математической функцией, которую вы хотите «свести к минимуму сумму». Вот тот же самый скелет кода, но с функцией «квадрат разности» вместо «разницы»:
int v[] = { 5, 6, 4, 3, 2, 6, 7, 9, 3 };
auto sum_of_squared_differences = [v](int xk) {
int result = 0;
for (auto&& xi : v) {
result += (xi - xk) * (xi - xk);
}
return result;
};
int closest_element_to_the_mean =
std::min_element(std::begin(v), std::end(v), [](int xa, int xb) {
return sum_of_squared_differences(xa) < sum_of_squared_differences(xb);
});
В этом случае мы можем также найти улучшенный алгоритм; а именно, вычислить среднее фронт и только после сканирования массива в поисках элемента, который ближе всего к этому виду:
int v[] = { 5, 6, 4, 3, 2, 6, 7, 9, 3 };
double actual_mean = std::accumulate(std::begin(v), std::end(v), 0.0)/std::size(v);
auto distance_to_actual_mean = [=](int xk) {
return std::abs(xk - actual_mean);
};
int closest_element_to_the_mean =
std::min_element(std::begin(v), std::end(v), [](int xa, int xb) {
return distance_to_actual_mean(xa) < distance_to_actual_mean(xb);
});
(PS - помните, что ни из вышеуказанных фрагментов кода не следует использовать на практике, если вы абсолютно уверены, что вам не нужно заботиться о переполнении целых чисел, об ошибке округления с плавающей запятой и о множестве других проблем с математикой.)
Обратите внимание, что порядок, подразумеваемый '(z - xi)' эквивалентно тому, что подразумевается '| z - xi |', но использование 'abs (z - xi)', вероятно, будет более эффективным, чем квадратизация '(z - xi)'. –