2014-12-16 2 views
2

Если я хочу найти median (это эквивалентно минимизации функции | г - х я |), можно использовать следующие code snippet:Минимизация (г-XI)^2

std::vector<int> v{5, 6, 4, 3, 2, 6, 7, 9, 3}; 

std::nth_element(v.begin(), v.begin() + v.size()/2, v.end()); 
std::cout << "The median is " << v[v.size()/2] << '\n'; 

Есть что-то вроде этого, чтобы найти "median" для минимизации (zx i)^2? То есть, я хочу найти элемент массива, в котором сумма этих функций будет минимальной.

+0

Обратите внимание, что порядок, подразумеваемый '(z - xi)' эквивалентно тому, что подразумевается '| z - xi |', но использование 'abs (z - xi)', вероятно, будет более эффективным, чем квадратизация '(z - xi)'. –

ответ

1

Учитывая массив х , х , & hellip ;, х п целых чисел, реальное число г, что сводит к минимуму ∑ я ∈ {1,2, & hellip;, п} (г - х я) это означает, г * = (1/N) ∑ я ∈ {1,2, & hellip;, п} х я. Вы хотите позвонить std::min_element с помощью компаратора, который обрабатывает x i меньше, чем x j тогда и только тогда, когда | n x i - n z * | < | n x j - n z * | (мы используем n z * = ∑ i ∈ {1,2, & hellip;, n} x i, чтобы избежать арифметики с плавающей запятой, есть способы уменьшить требуемую дополнительную точность).

+0

вы абсолютно правы. Я нашел производную функции. – Denis

3

Если вы хотите найти nth_element() согласно предиката сравнения (z - xi)^2 можно просто добавить соответствующую логику бинарного предиката вы можете передать в nth_element():

auto trans = [=](int xi){ return (z - xi) * (z - xi); }; 
std::nth_element(v.begin(), v.begin() + v.size()/2, v.end(), 
    [&](int v0, int v1) { return trans(v0) < trans(v1); }); 

Из вопроса не ясно z или xi является изменяющейся переменной. По внешнему виду я предположил, что xi должен быть x i. Если z меняется, просто переименуйте аргумент в лямбда trans (который я просто также дал = в захвате ...).

+0

благодарит за ответ. К сожалению, это немного не так. 'z' не является константой. 'z' - это элемент массива, где достигается минимум. – Denis

+0

@Denis Вы должны уточнить, что в вашем вопросе –

+0

Итак, 'xi' является константой? Я предположил, что вы имеете в виду 'x' i как переменную. Просто переименуйте аргумент ... –

2

Ваш вопрос работает, по крайней мере, два различных уровня: Вы спрашиваете, как реализовать определенный алгоритм идиоматический в 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 - помните, что ни из вышеуказанных фрагментов кода не следует использовать на практике, если вы абсолютно уверены, что вам не нужно заботиться о переполнении целых чисел, об ошибке округления с плавающей запятой и о множестве других проблем с математикой.)

+0

ваш ответ велик! Спасибо! – Denis

Смежные вопросы