Fractional Cascading - Example

Example

As a simple example of fractional cascading, consider the following problem. We are given as input a collection of k ordered lists Li of real numbers, such that the total length Σ|Li| of all lists is n, and must process them so that we can perform binary searches for a query value q in each of the k lists. For instance, with k = 4 and n = 17,

L1 = 2.4, 6.4, 6.5, 8.0, 9.3
L2 = 2.3, 2.5, 2.6
L3 = 1.3, 4.4, 6.2, 6.6
L4 = 1.1, 3.5, 4.6, 7.9, 8.1

The simplest solution to this searching problem is just to store each list separately. If we do so, the space requirement is O(n), but the time to perform a query is O(k log(n/k)), as we must perform a separate binary search in each of k lists. The worst case for querying this structure occurs when each of the k lists has equal size n/k, so each of the k binary searches involved in a query takes time O(log(n/k)).

A second solution allows faster queries at the expense of more space: we may merge all the k lists into a single big list L, and associate with each item x of L a list of the results of searching for x in each of the smaller lists Li. If we describe an element of this merged list as x where x is the numerical value and a, b, c, and d are the positions (the first number has position 0) of the next element at least as large as x in each of the original input lists (or the position after the end of the list if no such element exists), then we would have

L = 1.1, 1.3, 2.3, 2.4, 2.5, 2.6,
3.5, 4.4, 4.6, 6.2, 6.4, 6.5,
6.6, 7.9, 8.0, 8.1, 9.3

This merged solution allows a query in time O(log n + k): simply search for q in L and then report the results stored at the item x found by this search. For instance, if q = 5.0, searching for q in L finds the item 6.2, from which we return the results L1 = 6.4, L2 (a flag value indicating that q is past the end of L2), L3 = 6.2, and L4 = 7.9. However, this solution pays a high penalty in space complexity: it uses space O(kn) as each of the n items in L must store a list of k search results.

Fractional cascading allows this same searching problem to be solved with time and space bounds meeting the best of both worlds: query time O(log n + k), and space O(n). The fractional cascading solution is to store a new sequence of lists Mi. The final list in this sequence, Mk, is equal to Lk; each earlier list Mi is formed by merging Li with every second item from Mi + 1. With each item x in this merged list, we store two numbers: the position resulting from searching for x in Li and the position resulting from searching for x in Mi + 1. For the data above, this would give us the following lists:

M1 = 2.4, 2.5, 3.5, 6.4, 6.5, 7.9, 8, 9.3
M2 = 2.3, 2.5, 2.6, 3.5, 6.2, 7.9
M3 = 1.3, 3.5, 4.4, 6.2, 6.6, 7.9
M4 = 1.1, 3.5, 4.6, 7.9, 8.1

Suppose we wish to perform a query in this structure, for q = 5. We first do a standard binary search for q in M1, finding the value 6.4. The "1" in 6.4, tells us that the search for q in L1 should return L1 = 6.4. The "5" in 6.4 tells us that the approximate location of q in M2 is position 5. More precisely, binary searching for q in M2 would return either the value 7.9 at position 5, or the value 6.2 one place earlier. By comparing q to 6.2, and observing that it is smaller, we determine that the correct search result in M2 is 6.2. The first "3" in 6.2 tells us that the search for q in L2 should return L2, a flag value meaning that q is past the end of list L2. The second "3" in 6.2 tells us that the approximate location of q in M3 is position 3. More precisely, binary searching for q in M3 would return either the value 6.2 at position 3, or the value 4.4 one place earlier. A comparison of q with the smaller value 4.4 shows us that the correct search result in M3 is 6.2. The "2" in 6.2 tells us that the search for q in L3 should return L3 = 6.2, and the "3" in 6.2 tells us that the result of searching for q in M4 is either M4 = 7.9 or M4 = 4.6; comparing q with 4.6 shows that the correct result is 7.9 and that the result of searching for q in L4 is L4 = 7.9. Thus, we have found q in each of our four lists, by doing a binary search in the single list M1 followed by a single comparison in each of the successive lists.

More generally, for any data structure of this type, we perform a query by doing a binary search for q in M1, and determining from the resulting value the position of q in L1. Then, for each i > 1, we use the known position of q in Mi to find its position in Mi + 1. The value associated with the position of q in Mi points to a position in Mi + 1 that is either the correct result of the binary search for q in Mi + 1 or is a single step away from that correct result, so stepping from i to i + 1 requires only a single comparison. Thus, the total time for a query is O(log n + k).

In our example, the fractionally cascaded lists have a total of 25 elements, less than twice that of the original input. In general, the size of Mi in this data structure is at most

as may easily be proven by induction. Therefore, the total size of the data structure is at most

as may be seen by regrouping the contributions to the total size coming from the same input list Li together with each other.

Read more about this topic:  Fractional Cascading

Famous quotes containing the word example:

    Our intellect is not the most subtle, the most powerful, the most appropriate, instrument for revealing the truth. It is life that, little by little, example by example, permits us to see that what is most important to our heart, or to our mind, is learned not by reasoning but through other agencies. Then it is that the intellect, observing their superiority, abdicates its control to them upon reasoned grounds and agrees to become their collaborator and lackey.
    Marcel Proust (1871–1922)