views:

303

answers:

2

Hi,

how do I limit the search in a boost::multi_index by the result of a previous search? As an example: suppose I have a rectangle class with an internal value like this:

    class MyRect
    {
    public:
        int    width;  
        int    height; 

        double value;
    }

and I need a data structure of such object to answer queries like "given an input_rectangle - which object MyRect is contained in that rectangle and has the highest value?"

I could use a 'multi_index' like this:

    struct given_value{};
    struct given_width{};
    struct given_height{};

    typedef multi_index_container<MyRect,
        indexed_by<
            ordered_non_unique< tag<given_value>, 
                member<MyRect, double, &MyRect::value>,
            ordered_non_unique< tag<given_width>, 
                member<MyRect, int, &MyRect::width>,
            ordered_non_unique< tag<given_height>, 
                member<MyRect, int, &MyRect::height>, >
        >
    > MyDataStructure;

    typedef MyDataStructure::index<given_width>::type MyDataStructureGivenWidth;
    typedef MyDataStructureGivenWidth::iterator WidthIterator;

If my input_rectangle has width input_width I could use something like this:

WidthIterator start_iter = data_object.get<given_width>().begin();
WidthIterator end_iter   = data_object.get<given_width>().upper_bound(input_width);

But how do I limit the search for the coresp height by the two given iterators? (And after that find the object with the highest value in that result?)

A: 

If I understand your problem correctly, there may be a simpler solution. Just put your MyRects into an STL-Set ordered by value (need to define comparison operator or custom comparison function). The you can create a custom predicate that and use that checks if a given MyRect is within a certain range. Then you use the STL-Algorithm find_if, and hand it the custom predicate. If you make sure that it traverses the sequence in decreasing order (e.g. by using reverse_iterator), it should return the MyRect you are looking for.

Hope that is understandable and applies to your problem.

Space_C0wb0y
Hi, thank you for your answer. I'm not sure, if I understand you correctly: You want to use an `std::set` which uses the `value` as an ordering, right? Then you want iterate the whole set (starting from the highest value) until you find an object which is contained in the `input_rectangle` ? This would be to slow for my purpose (linear time). Searching in an ordered set is logarithmic. If I can tell the multi-index to search (for height) in a given range of another search-index (the width) then the whole search could be logarithmic. But I don't know if that is possible..
Dane
If your queries are always of the same pattern (and always have the same ordering of the arguments), the fastet (in terms of complexity) solution would be an index that first orders by width and then by height. This would be like a Set of Sets, where each inner Set contains all Rects of a fixed width ordered by height, and the out Set contains the inner Sets ordered by their corresponding width.
Space_C0wb0y
ahh, a lexicographical ordering (this I could implement with a `std::set`). The fastest implementation in my mind would be a data structure with random-access for containment (i.e. if you have a maximal width and height, you can have `max_width * max_height` many entries with the coresp `MyRects` or values stored) then you just have to iterate all possible widths and height which are contained in `input_rectangle` and remember the highest value. (NOTE: this is linear in number of rects already contained in `input_rectangle` - is there a logarithmic way?). BUT this consumes way to much memory.
Dane
A: 

I don't think you can do an inplace limitation.
Store the resulting iterators of the matching widths query in another container and use that container to find the matching heights with remove_if. Then use max_element to find the largest.

If you store the elements as pointers, you could use the same MIC to store the results.

stefaanv
Hi and thank you for your answer. I think you're right, that an in-place limitation will require more than constant time. Although your last hint is quite elegant I hoped that boost is able to do this internally. (for example: if I found an range `[start_iter,end_iter)` then I want the MIC to forget all other elements temporarily (or in a copy) and reconstruct itself for searches with all other indecies). If there is no such internal method then I can iterate my range directly (because construction of such ordered data structures is at least linear like `std::make_heap`, right?)
Dane
You're right: you will have to find out which system works best for you. Note however that MIC would probably have to do something similar in order to make a subview.
stefaanv
Yeah, I'm afraid an internal method would just do the same thing. But there is this tiny possibility that there is a way to handle this effectively by an internal method. And it would be great if I don't have to do it manually, of course. Therefore I leave this question open for a while - perhaps somebody else has another idea. But thank you (both) for you answers!
Dane
I found here [#1] such a complex search within the documented examples. The approach is similar to the one stefaaanv suggested. Just wanted to update the previous comment in case anyone cares in the future. Thanks everyone!(#1 = http://www.boost.org/doc/libs/1_39_0/libs/multi_index/doc/examples.html#example6)
Dane