Библиотека minmax состоит из двух заголовкови(см.обоснование). Проблема, решаемая этой библиотекой, заключается в том, что одновременные вычисления min и max требуют только одного сравнения, но с использованиемstd::minиstd::maxСравнение двух сил. Хуже того, для вычисления минимальных и максимальных элементов диапазонаnэлементов требуется только3n/2+1сравнения, вместо2n(в двух проходах), вынужденныхstd::min_elementиstd::max_element. Я всегда думал, что это пустая трата, чтобы вызвать две функции, чтобы вычислить степень диапазона, выполняя два прохода над входом, когда одного должно быть достаточно. Библиотека решает обе проблемы.
Первый файл реализует шаблоны функцийminmaxкак простые расширения стандарта C++. Когда он возвращает паруconst&, мы должны использовать библиотекуBoost.tupleдля создания таких пар. (Обратите внимание: намерение состоит не в том, чтобы исправить известные по умолчаниюstd::minиstd::max, а в том, чтобы добавить еще один алгоритм, который объединяет оба; см.обоснование.)
Второй файл реализует функциональные шаблоныminmax_element. Во второй части также предлагаются варианты, которые обычно не могут быть вычислены алгоритмом minmax, и которые являются более гибкими в случае, если некоторые элементы равны. Эти варианты могли быть также обеспечены политическим дизайном, но я вынес решение против этого (см.обоснование).
Если вас интересует производительность, вы увидите, чтоminmax_elementнемного менее эффективен, чем одинmin_elementилиmax_element, и, таким образом, в два раза эффективнее двух отдельных вызововmin_elementиmax_element. С теоретической точки зрения все функцииminmax_elementвыполняют максимум3n/2+1сравнения и ровно n приращенийForwardIterator.
I won't bore you with the complete synopsis, they have exactly the same
declaration as their corresponding _element function. Still,
you can find the complete synopsis here.
First_min_elementиfirst_max_elementнаходят наименьшие и наибольшие элементы в диапазоне[первый, последний]. Если таких элементов несколько, то первый возвращается. Они идентичныstd::min_elementиstd::max_elementи включены в эту библиотеку только для симметрии.
Последний_мин_элементипоследний_макс_элементнаходят наименьшие и наибольшие элементы в диапазоне[первый, последний]. Они почти идентичныstd::min_elementиstd::max_element, за исключением того, что они возвращают последний экземпляр самого большого элемента (а не первый, какfirst_min_elementиlast_max_element).
Семейство алгоритмов, включающихпервый_min_first_max_element,первый_min_last_max_element,последний_min_first_max_elementипоследний_min_last_max_element, можно описать в общем виде следующим образом (используя, который, чтодляпервогоилипоследнего):, который_min_, что_max_element, который, который, который, который наименьший элемент и (первый или последний, согласно, что, что) самый большой элемент в диапазоне[первый, последний], самый большой элемент в диапазонеПервая версия семантически эквивалентна:<
Примечание:первый_min_last_max_elementтакже можно описать как нахождение первого и последнего элементов в диапазоне, если бы он был стабильно отсортирован.
Мы не поддерживаем такие идиомы, какпривязка(a,b)=minmax(a,b)для заказа двух элементовa,b, хотя это имело бы желаемый эффект, если бы мы вернули ссылку вместо постоянной ссылки. Причина в том, что два ненужных задания выполняются, если a и b в порядке. Лучше придерживаться, если (bдля достижения этого эффекта.
Эти алгоритмы всегда выполняют не менее3n/2-2сравнений, что в любом случае является нижней границей по количеству сравнений (Кормен, Лейзерсон, Ривест: «Введение в алгоритмы», раздел 9.1, Упражнение 9.1-). Алгоритмы по существу сравнивают элементы в парах, выполняя 1 сравнение для первых двух элементов, затем 3 сравнения для каждой оставшейся пары элементов (один для заказа элементов и один для обновления каждого минимума и максимума). Когда число элементов нечетное, последний необходимо сравнить с текущим минимумом, а также с текущим максимумом. Кроме того, дляminmax, в случаях, когда равенство двух участников в паре может произойти, и обновление хранит второе, мы сохраняем первое, чтобы проверить в конце, если обновление должно было сохранить первое (в случае равенства). Трудно предсказать, будет ли выполнено последнее сравнение или нет, следовательно, самое большее в обоих случаях.
Эти алгоритмы всегда выполняют не менее3n/2-2сравнений, что в любом случае является нижней границей по количеству сравнений. Метод такой же, как в примечании.выше, и, как и выше, когда число элементов нечетное, последний необходимо сравнить с текущим минимумом, а также с текущим максимумом. Мы можем избежать последнего сравнения, если первое успешно, следовательно,самое большеевместоточнов странном случае.
first_min_last_max_element, иlast_min_first_max_elementвыполняют точно3n/2-2сравнения, если n равно и не равно нулю, и самое большее3(n/2), еслиnнечетно,
where n is the number of elements in [first,last).
[1] We do not support
idioms such as tie(a,b)=minmax(a,b)
to order two elements a, b, although this would have
the desired effect if we returned a reference instead of a constant
reference. The reason is that two unnecessary assignments are
performed if a and b are in order. It is better to stick to if (b<a)
swap(a,b) to achieve that effect.
[2] These algorithms always
perform at least 3n/2-2 comparisons, which is a lower bound on
the number of comparisons in any case (Cormen, Leiserson, Rivest: "Introduction
to Algorithms", section 9.1, Exercise 9.1-). The algorithms essentially compare
the elements in pairs, performing 1 comparison for the first two elements,
then 3 comparisons for each remaining pair of elements (one to order the
elements and one for updating each the minimum and and the maximum). When
the number of elements is odd, the last one needs to be compared to the
current minimum and also to the current maximum. In addition, for minmax,
in cases where equality of the two members in the pair could occur, and
the update stores the second, we save the first to check at the end if
the update should have stored the first (in case of equality). It's hard
to predict if the last comparison is performed or not, hence the at most
in both cases.
[3] These algorithms always
perform at least 3n/2-2 comparisons, which is a lower bound on
the number of comparisons in any case. The method is the same as in note
[2]
above, and like above, when the number of elements is odd, the last one
needs to be compared to the current minimum and also to the current maximum.
We can avoid the latter comparison if the former is successful, hence the
at
most instead of exactly in the odd case.
В первой версии библиотеки я предложил_ifверсии всех алгоритмов (ну, не всех, потому что это было бы слишком много). Однако для этого просто нет причин, и все версии, которые у меня были, были так же быстро реализованы с использованием отличногоБиблиотека. А именно, призыв кmin_element_if (первый, последний, пред)будет так же хорошо реализован:<
// equivalent to min_element_if(first, last, pred)
min_element(boost::make_filter_iterator(first, last, pred),
boost::make_filter_iterator(last, last, pred));
>Возможно, версияmin_element_ifнесколько короче, но накладные расходы на адаптеры итератора невелики, и они избавляются от большого количества кода (подумайте обо всех комбинациях между первым / последним и удвоите их с вариантами _if!).
Discussion: about std::max_element
Это обоснование несколько исторично, но объясняет, почему все эти функцииfirst/last_min/max_element.
Стандарт C++ требует, чтобыstd::min_elementиstd::max_elementвозвращали первый экземпляр наименьших и крупнейших элементов (в отличие, скажем, от последнего). Этот произвольный выбор имеет некоторую последовательность: В случае v типа vector, например, верно, чтоstd::min_element(v.begin(),v.end(),std::less()) == std::max_element(v.begin(),v.end(),std:: greater()).
Конечно, в этом нет ничего плохого: это просто вопрос выбора. Еще один способ определить min_element и max_element - определить их как первый и последний элементы, если диапазон был стабильно отсортирован. (Сортстабильныйнеобходим для разграничения итераторов, имеющих одинаковое значение.) В этом случае мин должен вернуть первую инстанцию, а макс - последнюю. Затем обе функции связаныreverse_iterator(std::first_min_element(v.begin(),v.end(),std::less())) == std::last_max_element(v.rbegin(),v.rend(),std:: greater()). Это определение тонко отличается от предыдущего.
Проблема определения возникает, когда человек пытается спроектировать элемент minmax_element, используя процедуру, предложенную в (Cormen, Leiserson, Rivest: «Введение в алгоритмы», раздел 9.1). Ондолжениметь возможность вывести алгоритм, используя только3n/2сравнения, если[первый, последний]имеетnэлементов, но если попытаться написать функцию, называемуюfirst_min_first_max_element(), которая возвращает обаstd::min_elementиstd::max_elementв паре, тривиальная реализация не работает. Проблема, довольно тонко, заключается в равных элементах: Мне пришлось некоторое время думать, чтобы найти способ выполнить только три сравнения на пару и вернуть первые мин и первые макс элементы. Долгое время казалось, что любая попытка сделать это потребует четырех сравнений на пару в худшем случае. Эта реализация достигает трех.
Невозможно (или даже желательно) изменить значениеmax_element, но все же полезно предоставить функцию под названиемminmax_element, которая возвращает паруmin_elementиmax_element. Хотя достаточно легко назватьmin_elementиmax_element, это выполняет2(n-1)сравнения и требуетдвухпроходов над входом. Напротив,minmax_elementбудет выполнять меньшее количество сравнений и выполнятьодиночныйпроход над входом. Экономия может быть значительной, когда тип итератора не является исходным указателем или даже просто моделью концепции InputIterator (хотя в этом случае интерфейс должен быть изменен, поскольку тип возврата не может быть скопирован, поэтому можно, например, вернуть значение).
Чтобы извлечь выгоду из всех вариантов алгоритма, я предлагаю ввести какfirst_min_elementиlast_min_element, так и их аналогиfirst_max_elementиlast_max_element. Затем я также предлагаю все варианты алгоритмов:first_min_last_max_elementиlast_min_first_max_element, которые выполняют только самое большее3n/2сравнения, и только один проход на входе. Фактически, можно доказать, что для вычисления minmax требуется по меньшей мере3(n/2)-2сравнения в любом случае проблемы (Cormen, Leiserson, Rivest, 2-е издание, раздел 9.1). Реализация, которую я даю, не выполняет ненужных сравнений (результат которых мог быть вычислен по транзитивности от предыдущих сравнений).
Похоже, чтопервый_min_last_max_elementможет быть немного медленнее, чемтолько первый_min_element, все еще намного меньше, чемпервый_min_elementипоследний_max_element, называемый отдельно
. Почему алгоритмы, а не аккумуляторы?
Алгоритмы minmax полезны для вычисления степени диапазона. В компьютерной графике нам нужен ограничивающий ящик из набора объектов. В этом случае необходимость в одном проходе еще более жесткая, поскольку все три направления должны быть сделаны сразу. Пища для размышлений: существует материя для хорошей общей библиотеки программирования со стекируемымиupdate_minиupdate_maxфункциональными объектами, которые хранят ссылку наmin_resultиmax_resultпеременных, в сочетании сfor_eachалгоритмом.
Я считаю, что многие стандартные последовательные алгоритмы могут быть переформулированы с помощью аккумуляторов (и многие другие, такие как статистика, ожидание / дисперсия / и т. Д.). Кажется, что есть место для другой библиотеки, но я не вижу, чтобы она конкурировала с minmax, а скорее распространяла несколько алгоритмов (включая minmax) на каркас аккумулятора. Тем не менее, я чувствовал, что это выходит за рамки этой библиотеки, чтобы предоставить такие аккумуляторы.
My students in CS903 (Polytechnic Univ., http://photon.poly.edu/~hbr/cs903/)
who had minmax_element as an assignment helped clarify the issues,
and also come up with the optimum number of comparisons for first_min_last_max_element.
The identification of the issue surrounding max_element is solely
my own.
One minmax_element implementation, which performs 3(n/2)+O(log
n) comparisons on the average when the elements are random_shuffled,
was suggested by my student Marc Glisse. The current one, which performs
3(n/2)+1
comparisons in the worst case, was suggested by John Iacono.
Finally, Matthew Wilson and Jeremy Siek contributed pre-review
comments, while Gennadiy Rozental, John Maddock, Craig Henderson, Gary
Powell participated in the review of the library, managed by Thomas
Witt. In particular, Gennadiy suggested a factorization of the code;
while I haven't followed it all the way, his suggestions do make the
code more readable and still work with older compilers.
Late after the review, as I finally scrounged to add the library for a
release, Eric Niebler noted the bad behavior of std::pair for
minmax and suggested to use Boost.tuple instead.
All my thanks for the excellent advice and reviews from all.
Статья Boost Minmax library раздела может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.