Notes : The logarithmic method

The topic of this lecture is about indexing. Namely, given an input set S of elements, we would like to preprocess S by creating a data structure on it. Then, given any query, we can utilize the structure to answer it efficiently, in particular, with much lower cost than if we did not have the structure. As an example, let us recall when the binary search tree – a fundamental structure at the undergraduate level – would be useful. Let S be a set of real values; given a real value q, a predecessor query finds the maximum value in S that are at most q. To support such queries efficiently, we can create a binary search tree on S, after which any query can be answered in O(lg n) time where n = |S|. This is much faster than answering q without using any structure – in that case, O(n) time is compulsory. We consider only decomposable problems. Formally, assume that S is divided into two disjoint partitions: S1, S2, namely, S1 ∩S2 = ∅ and S1∪S2 = S. A query on S is decomposable if the answer can be obtained in constant time from the answers of the same query on S1 and S2, respectively. For example, the predecessor problem mentioned earlier is clearly decomposable: to find the predecessor in S of a real value q, we can first find the predecessors of q in S1, S2 respectively, and then return the larger of those two predecessors. In many applications, we need to make our structure dynamic. Namely, we want to support two types of updates: (i) insertion, where an element e is added in S, and (ii) deletion, where an element existing in S is removed – in both cases, the structure on S must be modified accordingly, so that it is still a valid index on the updated S. For the predecessor problem, the binary search tree can be updated in O(lg n) time per insertion and deletion. For some problems, however, designing a dynamic structure that supports fast updates can be rather difficult. The nearest neighbor problem is an example. In this problem, we are given a set S of n two-dimensional points in R. Given a point q ∈ R, we want to find the data point in S that is nearest to q by Euclidean distance. There is an elegant, static, structure based on the Voronoi diagram [3], which consumes O(n) space, and answers each query in O(lg n) time. Unfortunately, updating a Voronoi diagram turns out to be rather challenging, and as a result, making the nearest neighbor structure dynamic very difficult. There has been some recent breakthrough on this problem [2], where a dynamic structure that can be updated in O(lg n) time is given. The structure is quite theoretical, and is not easy to implement. It turns out that life is much easier if we only want our structure to be semi-dynamic, that is, we want to perform only insertions, but not deletions. Although not as useful as (fully) dynamic structures, semi-dynamic indexes also play an important role in practice, when the dataset of the underlying application only expands, but never shrinks. We will learn in this lecture a clever technique called the logarithmic method that can convert a static structure to a semi-dynamic structure. The only requirement is that the structure can be constructed efficiently. The static nearest neighbor structure mentioned earlier can be built in O(n lg n) time. We will see that the logarithmic method permits us to obtain (quite effortlessly) a semi-dynamic structure that consumes