CAPÍTULO 2: QUÉ ES EL MERCHANDISING
2.2. Historia del visual merchandising
Now we can use our partition point function to perform binary search. If we have total ordering, any sorted range is partitioned for any value a according to the predicate x < a. STL actually provides a few functions that perform binary search, depending on our task. If we want to find the first position where the item is found, we use the lower_bound function. Using the features in C++11, we can write lower_bound like this:
Click here to view code image
template <ForwardIterator I>
I lower_bound(I f, I l, ValueType<I> a) { return partition_point(f, l,
[=](ValueType<I> x) { return x < a; }); }
The last line defines a new anonymous function (also known as a lambda expression 16 ) that returns true if its
argument is less than a, then passes that function as the predicate used by partition_point. The
lower_bound function returns the position of the item a, or the position it would be in if it were there. ValueType is just a shorthand type function for accessing the appropriate iterator trait, like the one we wrote earlier for DifferenceType:
Click here to view code image
template <InputIterator I>
using ValueType = typename std::iterator_traits<I>::value_type;
16 See Appendix C for details on how to use lambda expressions.
In the case where the returned position is not l, we still need to know whether we found the item. To do this, the caller needs to check whether the dereferenced return value is equal to a.
If instead we want to find the last position where the item is found, we use upper_bound. The code is almost the same, except that we define the predicate to check if the value is less than or equal to a, rather than strictly less than a:
Click here to view code image
template <ForwardIterator I>
I upper_bound(I f, I l, ValueType<I> a) { return partition_point(f, l,
[=](ValueType<I> x) {return x <= a;}); }
Some readers may be wondering which function is the “real” binary search. The answer is that it depends on the task. If you want to find the position of the first matching element, then lower_bound is the “real” binary search. If you want to find the position of the last matching element, then it’s upper_bound. If you want to know the entire range of matching elements, STL provides a third function, equal_range. And if all you care about is whether there was a match, there is a function binary_search—but keep in mind that all it’s doing is calling lower_bound and testing whether the dereferenced return value is equal to the item.
The additional functionality of equal_range clearly benefits from the STL convention of using semi-open ranges. Even when the element is not present, the function returns an empty range at the position where it could be inserted.
10.9 Thoughts on the Chapter
We began this chapter by seeing how Aristotle’s levels of abstraction (individual, species, genus) correspond to the programming notions of instance, type, and concept. It is the notion of concept that allows a generic program to work in a variety of settings.
One of your central goals as a programmer should be to identify existing concepts in your application. You will often develop new algorithms, occasionally develop a new data structure, and only rarely define a new
concept. In that rare situation, a lot of work is needed to ensure that it is a true concept and not just a collection of unrelated requirements. To restate Occam’s Razor, one should not introduce new concepts without necessity.
We then introduced the concept of iterators, and saw the role they play in some fundamental algorithms. By using compile-time type dispatch on different kinds of iterators, we can ensure that the most efficient implementation gets executed in a given situation.
Finally, we saw the importance of writing not only correct code, but also correct interfaces. An incorrect interface can severely limit the utility of a function; a correct interface allows it to be used in a variety of situations without loss of efficiency.
11. Permutation Algorithms
An algorithm must be seen to be believed. Donald KnuthComplex computer programs are built up from smaller pieces that perform commonly used fundamental tasks. In the previous chapter, we looked at some tasks involving searching for data. In this chapter, we’ll look at tasks that involve shifting data into new positions, and show how to implement them in a generic way. We’ll see how these tasks also end up using two ideas discussed earlier in the book: groups from abstract algebra and the greatest common divisor (GCD) from number theory.
The tasks we will focus on—rotate and reverse—allow us to introduce algorithms that do the same task differently depending on the concept of the iterator to which they apply. In addition to illustrating some generic programming techniques, these algorithms are of great practical importance. The rotate algorithm in particular is probably the most used algorithm inside the implementation of STL components from vector to stable_sort.
11.1 Permutations and Transpositions
Our exploration of the GCD algorithm led us to learn about groups and other algebraic structures. Using this knowledge, we’re going to start investigating the mathematical operations permutation and transposition, which play an important role in some fundamental algorithms.
Definition 11.1. A permutation is a function from a sequence of n objects onto itself.