Lemmings Forums

Lix => Lix Main => Topic started by: Simon on May 24, 2016, 06:53:38 PM

Title: Algorithm: Smallest container on torus
Post by: Simon on May 24, 2016, 06:53:38 PM
Hi,

what is a smart algorithm for this?

Input data: List L of rectangles. Rectangle is 4 ints: (x, y) of top-left corner, (xl, yl) the two lengths. Also input data is exactly one torus map M -- this is a large rectangle, it's larger than any single rectangle in L.

Wanted output: Rectangle O that surrounds all the listed rectangles. This rectangle should be as small as possible. It's allowed to move the rectangles in L by integer multiples of the length of M. The output rectangle O should account for O-minimizing optimal shifts of the rectangles in L. (To clarify after NaOH's question: I want to minimize the lengths of O. You may shift the rectangles of L as often as you wish to minimize the length of O.)

I know: You can separate this problem by dimension: Consider only x and x-length first, then y and y-length.

Existing algorithms: I have an algorithm to find the smallest container on a non-torus map -- when you're not allowed to move the rectangles in L by the length of M.

For example, if the rectangles are denoted as Rect(x, y, xl, yl), the non-torus algorithm computes the correct smallest container for the following list of three rectangles:

    [ Rect(3, 5, 10, 10), Rect(5, 5, 9, 9), Rect(1, 1, 1, 1) ]
    .reduce!smallestContainer == Rect(1, 1, 13, 14)


I also have an exptime algorithm for the torus-smallest container: For each rectangle in the input list, try whether to set its top-left point in the range [0, M.xl[ or into [M.xl, 2 * M.xl[ instead. With an input list of length n, we have 2n choices to try. For each choice, measure with the non-torus algorithm. Use the choice that minimizes the non-torus algorithm target value.

Can I do it in less than exptime?

-- Simon
Title: Re: Algorithm: Smallest container on torus
Post by: NaOH on May 24, 2016, 08:45:14 PM
Interesting problem, thinking about it.

Quote
The output rectangle O should account for O-minimizing optimal shifts of the rectangles in L.

I'm not sure what this means. Are we minimizing the size of the rectangle or the number of shifts?
Title: Re: Algorithm: Smallest container on torus
Post by: NaOH on May 24, 2016, 08:48:53 PM
How about something like: calculate the distances between each rectangle (edit) and the previous along rectangle (/edit) along a certain axis, then pick the largest of these gaps, shift everything to the left of the gap up by M, and apply the non-toroidal algorithm to the result?

Edit: a "gap" from a rectangle A to the previous rectangle B is A.x - B.x - B.xl, and the "previous rectangle B" is the rectangle with the largest (B.x+B.xl) value such that B.x < A.x

Edit2: and then special-case the rectangle with the least x coordinate.

And then do it again for y.
Title: Re: Algorithm: Smallest container on torus
Post by: Simon on May 24, 2016, 08:55:22 PM
Quote
Are we minimizing the size of the rectangle or the number of shifts?

We're minimizing the lengths of the output rectangle. We may shift as often as necessary.

My idea right now for a linear runtime: Recursively fold the list with the following function.

Rect foldTwoRects(Rect a, Rect b)
{
    // induction hypothesis: a is the output rectangle of the already-folded part of the list.
    Have the rectangle b top-left point in the interval [0, M[.
    Measure the container of a and b here.
    Move b ahead by M.
    Measure the container of a and b here.
    Move b back to where it was, and back by M again.
    Measure the container of a and b here.
    Return the smallest container of the 3 measured containers.
}

Quote
How about something like: calculate the distances between each rectangle along a certain axis, then pick the largest of these gaps, shift everything to the left of the gap up by M, and apply the non-toroidal algorithm to the result?

I like this, will think about it. This sounds more like my first shower ideas earlier today. My impl in this post feels too brute-force-ish.

-- Simon
Title: Re: Algorithm: Smallest container on torus
Post by: Simon on May 24, 2016, 09:19:31 PM
About gaps in one dimension: You care about the area that's not covered by any rectangle. This area is a collection of disjoint intervals, the gaps. You find the largest gap G, and herd the rectangles of L such that O spans the complement of G.

This sounds plausible. I have to track the list of gaps then, beginning with the full M, and hack the gaps apart according to the list of input rectangles.

Problem: When the rectangles leave no gaps whatsoever, where should we cut? This is an esoteric corner case in practice. I want to apply the algorithm to tile grouping, and grouping is best for small-scale cutting of few tiles. It's not designed to group map-spanning constructs. The recursive foldTwoRects seems to answer this case too, which is a nice-to-have.

-- Simon
Title: Re: Algorithm: Smallest container on torus
Post by: Simon on May 25, 2016, 05:55:57 AM
Folding with foldTwoRects is not correct sometimes when the gap-finding stays correct.

Assume the following spread of 1x1 rectangles on a 10x1 torus map:

a a a a a b     b a
0 1 2 3 4 5 6 7 8 9


Using foldTwoRects, if we gather pieces starting with the a at 0, going towards the right from there, we get the correct smallest container: The smallest container is the complement of the big gap.

But if we use foldTwoRects and start with the two bs, the algorithm will select the big gap in the very first iteration. Then, we never let the big gap go. We will leave out a small gap. Not even sorting the pieces by location would have helped us.

-- Simon
Title: Re: Algorithm: Smallest container on torus
Post by: Nepster on May 25, 2016, 05:01:32 PM
I will focus on the one-dimensional problem.

There you want to compute
   minR1 maxR2 ((R2.x - R1.x) mod M.Width + R2.xl)
where both R1 and R2 range over all rectangles in the list L. (In this post mod will always return a non-negative result, even if the input was negative)
Explanation:
(The actual position of the final rectangle O can be obtained from R1 and R2).

This gives an obvious algorithm of O(N^2), by computing the innermost expression for all R1, R2 and then finding the min-max for these values.

But we can do even better using the following algorithm:
In total this algo runs as O(N log N) with a few additional O(N) parts.

PS: I don't think I ever make use of the fact, that rectangles have width at most M.Width.
PPS: Is this assumption even true for tiles that are already groups themselves?
Title: Re: Algorithm: Smallest container on torus
Post by: Simon on May 26, 2016, 09:42:41 AM
Thanks! This is an excellent view of the problem. I have implemented the O(n2)-version in my code here. It's short, well-documented, and unittested. :lix-cool:

I want to draw GUI buttons for the grouping (http://asdfasdf.ethz.ch/~simon/etc/icon-terrain-grouping-2.png), then push everything to github these days.

I've understood how to generate SortedShortL and why it is good. I'm not using the improved algorithm yet. When grouping 10 to 20 tiles at most, tile allocation is responsible for most of the runtime, not the O(n2) algo. If people generate larger groups, I'll refine the algorithm.

Tiles must be shorter than M: It's perfect to not rely on this. Yes, it may be false on small maps with huge groups.

-- Simon
Title: Re: Algorithm: Smallest container on torus
Post by: ccexplore on May 26, 2016, 06:23:47 PM
Problem: When the rectangles leave no gaps whatsoever, where should we cut? This is an esoteric corner case in practice. I want to apply the algorithm to tile grouping, and grouping is best for small-scale cutting of few tiles. It's not designed to group map-spanning constructs.

Do agree it probably won't happen often in practice.  That said, you may still want to determine how much the output of such a corner case may confuse Lix and in turn, whether the user may need to be warned or outright disallowed about such constructs.

For example, it would seem that in that case, the algorithm will probably end up producing a rectangle larger than M (in the dimension(s) that have no gaps).  If you allow the grouping, that means you either have a group whose bounding rectangle exceeds the map's size, or you force the group's bounding rectangle to match the map size at which point it is no longer a traditional bounding rectangle (ie. the boundary will cut into one or more rectangles of the constituent tiles).
Title: Re: Algorithm: Smallest container on torus
Post by: Simon on May 26, 2016, 07:23:02 PM
The game allows tiles larger than the map.

Nepster's algo may produce groups larger than the map, and that's fine. Groups shouldn't depend on the map. We use the map in the algorithm because the editor is handy to specify groups, and the editor works on maps.

On a torus map, when a gigantic tile gets to draw to the same pixel multiple times due to wrapping, I don't specify what color should end up there.

Only concerning gigangic tiles, too: Earth, air or steel will be correctly set if (the tile has air or earth, no steel) or (the tile has air or steel, no earth). Groups may have both steel and earth, hm. Maybe there's a bug here where stuff looks different than what its physics is, maybe not. Let's keep it in the back of our mind.

-- Simon