Вот такая задача пришла мне из code review. Я хочу выбрать минимальное значение из набора на основе специального предиката сравнения. Как это:
struct Complex { ... };
float calcReduction(Complex elem);
Complex findMinValueWithPredicates(const std::vector<Complex>& values)
{
auto it = std::min_element(values.begin(), values.end(),
[](const Complex& a, const Complex& b) {
return calcReduction(a) < calcReduction(b);
});
if (it == values.end()) throw std::runtime_error("");
return *it;
}
Здесь я нахожу минимальный элемент на основе предиката. Этот предикат вычисляет приведение обоих значений к float
, а затем сравнивает эти числа с плавающей запятой. Работает нормально, выглядит аккуратно.
Вы видите проблему? Да, для набора из N
элементов calcReduction()
вызывается 2N
раз, при этом достаточно вычислить его только N
раз - по одному разу для каждого элемента.
Один из способов решить эту проблему — написать явные вычисления:
Complex findMinValueExplicit(const std::vector<Complex>& values)
{
float minReduction = std::numeric_limits<float>::max();
Complex minValue;
for (Complex value : values)
{
float reduction = calcReduction(value);
if (reduction < minReduction)
{
minReduction = reduction;
minValue = value;
}
}
if (minReduction == std::numeric_limits<float>::max()) throw std::runtime_error("");
return minValue;
}
Он отлично работает, и у нас есть только N
обращений к calcReduction()
. Однако это выглядит слишком многословно, а цель не столь ясна, по сравнению с явным вызовом min_element
. Потому что, когда вы вызываете min_element
, очень легко догадаться, что вы собираетесь найти минимальный элемент, знаете ли.
Единственная идея, которая у меня есть на данный момент, — это создать собственный алгоритм наподобие min_element_with_reduction
, принимающий диапазон и функцию редукции. Звучит разумно, но интересно, есть ли готовые решения.
Любые идеи о том, как решить эту задачу с четким намерением и некоторыми готовыми решениями? Повышение приветствуется. С++ 17 и диапазоны интересно посмотреть.
values.empty()
в первую очередь. Затем начните с первого элемента и введите цикл по оставшимся элементам. Нет, это, вероятно, не так очевидно, как ваш текущий код, но если вы оптимизируете этот код для максимальной скорости (вы профилировали это, верно?), некоторые компромиссы ИМХО приемлемы, особенно если полученный код правильно объяснен в комментариях. - person Ulrich Eckhardt   schedule 16.10.2015