I have a circle which i need to fill with rectangles.Piled one over the other.The rectangles are available in specific sizes only.And we are also given the number of rectangles we must put.I need to get the set of rectangle lengths which cover the most area of the circle.For eg if the circle has a diameter of 100,rectangles of lengths [100,95,90,85,...15,10,5] can be put.I have tried using a brute force method by parsing through all the possible combinations.It yields good results when the numbers are small.Another algorithm i tried is to restrict the range of lengths which each rectangle occupies.Like the first rectangle will have a length of 95 or 90 to give the best result.But even this method is cumbersome when the number of rectangles to be put is really high.Here is how the rectangles are arranged
If the first rectangle has a length l,and diameter of circle is d,its thickness is sqrt(d2-l2).The thickness of second one if its length is k is sqrt(d2-k2)-sqrt(d2-l2).
Is there any algorithm so that i can go formulate the results.
Why should a brute-force-attack on this problem be difficult? You just need to put some effort in your calculation code and I'm sure it will work fine. It just has 19 levels at maximum. This shouldn't be too complicated and will give you the result within ... well, some hours, as I just found out. 19 levels will result in 3.3e17 calculations.
About the algorithm:
With one rectangle, you get the largest covered area when the rectangle is a square. I think that's very easy to understand. The corner of the square is at 45° from the circle center (assuming that horizontal is 0°, but that actually doesn't matter as the whole structre is point symmetric), the size is (0.707*diameter)^2 = 5000.
The closest to width 70.7 is 70. In general I suggest checking the number below (70) and above (75) the accurate result (70.7). The area of your rectangle is 70 * 71.41 = 4999. (But it would be nice to know, if the height also has to be a value out of your 5's-grid!)
Now it's getting more difficult and I hope I am right:
As I write this answer, it turns out, I am not right. :-( The rounded values have a higher result than the theoretical maximum. But I will post it regardless, maybe it helps to find the real answer.
When you have 2 rectangles, the largest area to cover should be when
the corners of rect1 are at 30° (and 150°, 210°, 330°), and
the corners of rect2 are at 60° (and 120°, 240°, 300°).
The sizes would be:
rect 1: 0.866*dia * 0.5 *dia = 4330
rect 2: 0.5 *dia * 0.866*dia = 4330 - minus the overlap =>> 0.5*0.36*dia^2 = 1830
sum: 6160
rounded to the 5's grid:
rect 1, #1) 85*52.86 = 4478
rect 2: #1) 50*(86.60-52.86) = 1696.2 #2) 55*(83.52-52.86) = 1696.1 #3) 45*(89.30-52.86) = 1648
sums: 6173.87 // 6173.75 // 6126
rect 1, #2) 90*43.59 = 3923
rect 2: #1) 50*(86.60-43.59) = 2151 #2) 55*(83.52-43.59) = 2196 #3) 45*(89.30-43.59) = 2057
sums: 6074 // 6119 // 5980
The winner is combination 1.1: rect1 = 85, rect2 = 50.
Due to using rounded values, you have to check each combination of upper and lower (and exact if it is on the grid) value of each rectangle, resulting in a maximum of 3^n checks if n is the number of rectangles (except n=1). A brute force is not nicer, but maybe easier. (And as found out and written above, it maybe will return better results, as this method is inaccurate).
EDIT 1:
The formula for 1 rectangle (which results in a square) is:
A = x * sqrt(D²-x²)
calculate the maximum using the derivative of A:
A' = D²-2x² / sqrt(D²-x²) = 0
You can also find it here: http://oregonstate.edu/instruct/mth251/cq/Stage8/Lesson/rectangle.html
The formula for 2 rectangles is:
A = f(x,y) = x * sqrt(D²-x²) + y * [sqrt(D²-y²)-sqrt(D²-x²)]
( x = width of r1, y = width of r2 )
The formula for n rectangles depends on n unknown variables. So you need to calculate n partial derivatives. Have fun! (or consider brute force, as you already are given a grid and don't need to do iterations ;-) )
Brute force algorithm
amount of calculations:
levels calculations
1 19
2 19 + 19*18 = 361
...
5 19 + 19*18 + 19*18*17 + 19*18*17*16 + 19*18*17*16*15 = 1494559
...
10 3.7e11
15 6.3e15
19 3.3e19
C# (or C++):
double dDia = 100;
int nSizes = 20;
int nmax = 2; // number of rectangles
int main()
{
int n = 1;
double dArea = 0.0;
dArea = CalcMaxArea (n, 0);
}
double CalcMaxArea (int n, double dSizeYParent)
{
double dArea = 0.0;
double dAreaMax = 0.0;
for (int iRun = nSizes-n; iRun >= 1; iRun--)
{
double dSizeX = iRun * 5;
double dSizeY = Math.Sqrt(dDia * dDia - dSizeX * dSizeX) - dSizeYParent);
double dAreaThis = dSizeX * dSizeY;
double dAreaOthers = 0.0;
if (n < nmax)
dAreaOthers = CalcMaxArea (n+1, dSizeY);
if (dArea > dAreaMax)
dAreaMax = dArea;
}
}
VBA, to be used in MS Excel
Dim dDia As Double
Dim nmax As Integer
Dim nSizes As Integer
Sub main()
dDia = 100
nmax = 2
nSizes = 20
Dim n As Integer
Dim dArea As Double
n = 1
dArea = CalcMaxArea(n, 0)
End Sub
Function CalcMaxArea(n As Integer, dSizeYParent As Double) As Double
Dim dArea As Double
Dim dAreaMax As Double
dArea = 0
For iRun = nSizes - n To 1 Step -1
Dim dSizeX As Double
Dim dSizeY As Double
Dim dAreaThis As Double
Dim dAreaOthers As Double
dSizeX = iRun * 5
dSizeY = Sqr(dDia * dDia - dSizeX * dSizeX) - dSizeYParent
dAreaThis = dSizeX * dSizeY
dAreaOthers = 0
If n < nmax Then
dAreaOthers = CalcMaxArea(n + 1, dSizeY)
End If
dArea = dAreaThis + dAreaOthers
If dArea > dAreaMax Then
dAreaMax = dArea
End If
Next
CalcMaxArea = dAreaMax
End Function
Tested in VBA with the given values, got the same result: 6173.87.
Further code may be added to remember on which values the maximum as reached.
I read the question again, and realize I completely missed a couple of key points in your post. The picture confused me, but that isn't a good excuse. My previous suggestion in the comments was a completely bad idea. I'm sorry, and I hope you didn't send a lot of time looking into it. If I had to solve this problem, this is how I would do it, right or wrong.
So I have been thinking this problem over. The best solution I can think of is to use a search algorithm such as A*. A* its self it rather simple to implement. I'm assuming you already have a method to calculate the area, which to me seems the hardest part. I have an idea of how I would go on with calculating the area of overlapping rectangles, but its the reason why I didn't write a program that could prove that my suggestion is a good one.
What I would do is have a master list of all of the potential rectangles.
Add to your frontier a copy of all rectangles not in the current path as the nth rectangle placed. This will allow you to set the width, and therefore calculate the area of the circle left to be filled. Keeping doing this, selecting the lowest cost path from the frontier each time, and after m nodes are explored, you should have the best fit. Where m is the total number of rectangles you must place.
For the cost evaluation, using the amount of space left to fill seems a natural choice. One thing to make note of though, is that the area left decreases over time, and you will need one that increases. I would think dividing the area left by the number of rectangles left should give you a nice cost function for finding the lowest cost path to the least area left in the circle. That one sounded good to me, but i'm sure there are others that could be used.
In regards to the heuristic, without a heuristic function you still have a best first search, so I would expect it to perform better than a blind brute force technique. With a good heuristic function, I would expect the performance to increase significantly. In thinking about what would make a good heuristic function, I thought that estimating the amount of the circle the rectangle would fill might work well. For instance, 10% of the area of the rectangle divided by the number of rectangles left to be placed. Since there is no pre-determined goal state, any estimate would have to be base solely off the area of the next rectangle. We know the full area of the rectangle won't contribute to solution. The majority of every rectangle after the first is wasted space as far as the solution goes, which is how i came up with that heuristic. As with the cost function, it seems like a reasonable idea to me, but if anyone can think of a better one, all the better.
There are all sorts of sites on A* out there, but here is one that looks well written. http://web.mit.edu/eranki/www/tutorials/search/
I hope this helps you out.
I know devising a working algorithm is complex, but this is the approach I thought of :
There could be only one rectangle which can occupy the maximum area in the circle with given diameter.
Find out the the Max width and height of rectangle that can be made to fit into the circle. There are a lot of solutions for the same. For example look: Find Largest Inscribed Rectangle This rectangle will then conclude a major portion of the max area.
The next task is then to fill the remaining portion of the circle with the rectangle of different sizes. Find out the best fit rectangle, as in the below image. This can be done by checking if the circle points lie inside the rectangle for a specified height and width
I again agree that this is very difficult to implement.
Related
I'm currently developing an application that will alert users of incoming rain. To do this I want to check certain area around user location for rainfall (different pixel colours for intensity on rainfall radar image). I would like the checked area to be a circle but I don't know how to do this efficiently.
Let's say I want to check radius of 50km. My current idea is to take subset of image with size 100kmx100km (user+50km west, user+50km east, user+50km north, user+50km south) and then check for each pixel in this subset if it's closer to user than 50km.
My question here is, is there a better solution that is used for this type of problems?
If the occurrence of the event you are searching for (rain or anything) is relatively rare, then there's nothing wrong with scanning a square or pixels and then, only after detecting rain in that square, checking whether that rain is within the desired 50km circle. Note that the key point here is that you don't need to check each pixel of the square for being inside the circle (that would be very inefficient), you have to search for your event (rain) first and only when you found it, check whether it falls into the 50km circle. To implement this efficiently you also have to develop some smart strategy for handling multi-pixel "stains" of rain on your image.
However, since you are scanning a raster image, you can easily implement the well-known Bresenham circle algorithm to find the starting and the ending point of the circle for each scan line. That way you can easily limit your scan to the desired 50km radius.
On the second thought, you don't even need the Bresenham algorithm for that. For each row of pixels in your square, calculate the points of intersection of that row with the 50km circle (using the usual schoolbook formula with square root), and then check all pixels that fall between these intersection points. Process all rows in the same fashion and you are done.
P.S. Unfortunately, the Wikipedia page I linked does not present Bresenham algorithm at all. It has code for Michener circle algorithm instead. Michener algorithm will also work for circle rasterization purposes, but it is less precise than Bresenham algorithm. If you care for precision, find a true Bresenham on somewhere. It is actually surprisingly diffcult to find on the net: most search hits erroneously present Michener as Bresenham.
There is, you can modify the midpoint circle algorithm to give you an array of for each y, the x coordinate where the circle starts (and ends, that's the same thing because of symmetry). This array is easy to compute, pseudocode below.
Then you can just iterate over exactly the right part, without checking anything.
Pseudo code:
data = new int[radius];
int f = 1 - radius, ddF_x = 1;
int ddF_y = -2 * radius;
int x = 0, y = radius;
while (x < y)
{
if (f >= 0)
{
y--;
ddF_y += 2; f += ddF_y;
}
x++;
ddF_x += 2; f += ddF_x;
data[radius - y] = x; data[radius - x] = y;
}
Maybe you can try something that will speed up your algorithm.
In brute force algorithm you will probably use equation:
(x-p)^2 + (y-q)^2 < r^2
(p,q) - center of the circle, user position
r - radius (50km)
If you want to find all pixels (x,y) that satisfy above condition and check them, your algorithm goes to O(n^2)
Instead of scanning all pixels in this circle I will check only only pixels that are on border of the circle.
In that case, you can use some more clever way to define circle.
x = p+r*cos(a)
y = q*r*sin(a)
a - angle measured in radians [0-2pi]
Now you can sample some angles, for example twenty of them, iterate and find all pairs (x,y) that are border for radius 50km. Now check are they on the rain zone and alert user.
For more safety I recommend you to use multiple radians (smaller than 50km), because your whole rain cloud can be inside circle, and your app will not recognize him. For example use 3 incircles (r = 5km, 15km, 30km) and do same thing. Efficiency of this algorithm only depends on number of angles and number of incircles.
Pseudocode will be:
checkRainDanger()
p,q <- position
radius[] <- array of radii
for c = 1 to length(radius)
a=0
while(a<2*pi)
x = p + radius[c]*cos(a)
y = q + radius[c]*sin(a)
if rainZone(x,y)
return true
else
a+=pi/10
end_while
end_for
return false //no danger
r2=r*r
for x in range(-r, +r):
max_y=sqrt(r2-x*x)
for y in range(-max_y, +max_y):
# x,y is in range - check for rain
Can anyone suggest any links, ideas or algorithms to generate flowers randomly like the one as my profile pic? The profile pic flower has only a 10 x 10 grid and the algorithm is not truly random. I would also prefer that the new algorithm use a grid of about 500 x 500 or even better, allow the user to pick the size of the grid.
[Plant[][] is declared as int plant[10][10];]
public void generateSimpleSky(){
for(int w2=0;w2<10;w2++)
for(int w3=0;w3<10;w3++)
plant[w2][w3]=5;
}
public void generateSimpleSoil(){
for(int q=0;q<10;q++)
plant[q][9]=1;
}
public void generateSimpleStem(){
int ry=rand.nextInt(4);
plant[3+ry][8]=4;
xr=3+ry;
for(int u=7;u>1;u--){
int yu=rand.nextInt(3);
plant[xr-1+yu][u]=4;
xr=xr-1+yu;
}
}
public void generateSimpleFlower(){
plant[xr][2]=3;
for(int q2=1;q2<4;q2++)
if((2-q2)!=0)
plant[xr][q2]=2;
for(int q3=xr-1;q3<=xr+1;q3++)
if((xr-q3)!=0)
plant[q3][2]=2;
}
It sounds like a reasonably simple problem where you just generate 1 parameter at a time, possibly based on the output of the previous variables.
My model of a flower will be: It has just a reasonably upright stem, a perfectly round center, some amount of leaves on the stem on alternating sides, petals perfectly distributed around the center.
random() is just a random number within some chosen bounds, the bounds may be unique for each variable. random(x1, x2, ..., xn) generates a random number within some bounds dependent on the variables x1, x2, ..., xn (as in stemWidth < stemHeight/2, a reasonable assumption).
The Stem
stemXPosition = width / 2
stemHeight = random()
stemWidth = random(stemHeight)
stemColour = randomColour()
stemWidthVariationMax = random(stemWidth, stemHeight)
stemWidthVariationPerPixel = random(stemWidth, stemHeight)
stemWidthVariationMax/-PerPixel are for generating a stem that isn't perfectly straight (if you want to do something that complicated, a low PerPixel is for smoothness). Generate the stem using these as follows:
pixelRelative[y-position][0] := left x-position at that y-position relative to the stem
pixelRelative[y-position][1] := right x-position at that y-position relative to the stem
pixelRelative[0][0] = randomInRange(-stemWidthVariationMax, stemWidthVariationMax)
for each y > 0:
pixelRelative[y-1][0] = max(min(randomInRange(pixel[y] - stemWidthVariationPerPixel,
pixel[y] + stemWidthVariationPerPixel),
-stemWidthVariationMax),
stemWidthVariationMax)
//pixelRelative[0][1] and pixelRelative[y-1][1] generated same as pixelRelative[y-1][i]
for each y:
pixelAbsolute[y][0] = width / 2 - stemWidth / 2 + pixelRelative[y][0]
pixelAbsolute[y][1] = width / 2 + stemWidth / 2 + pixelRelative[y][1]
You can also use arcs to simplify things and go more than 1 pixel at a time.
The Top
centerRadius = random(stemHeight)
petalCount = random() // probably >= 3
petalSize = random(centerRadius, petalCount)
It's not too easy to generate the petals, you need to step from 0 to 2*PI with step-size of 2*PI/petalCount and generate arcs around the circle. It requires either a good graphics API or some decent maths.
Here's some nicely generated tops of flowers, though seemingly not open-source. Note that they don't have a center at all. (or centerRadius = 0)
The Leaves
You could probably write an entire paper on this, (like this one) but a simple idea would just be to generate a 1/2 circle and extend lines outward from there to meet at 2*the radius of the circle and to draw parallel lines on the flower.
Once you have a leaf generation algorithm:
leafSize = random(stemHeight) // either all leaves are the same size or generate the size for each randomly
leafStemLength = random(leafSize) // either all leaves have the same stem length or generate for each randomly
leafStemWidth = random(leafStemLength)
leaf[0].YPosition = random(stemHeight)
leaf[0].XSide = randomly either left or right
leaf[0].rotation = random between say 0 and 80 degrees
for each leaf i:
leaf[i].YPosition = random(stemHeight, leaf[i-1]) // only generate new leaves above previous leaves
leaf[i].XSide = opposite of leaf[i].XSide
Last words
The way to determine the bounds of each random would be either to argue it out, or give it some fixed value, generate everything else randomly a few times, keep increasing / decreasing it until it starts to look weird.
10 x 10 versus 500 x 500 would probably require greatly different algorithms, I wouldn't recommend the above for below 100 x 100, maybe generate a bigger image and simply shrink it using averaging or something.
Code
I started writing some Java code, when I realised it may take a bit longer than I would like to spend on this, so I'll show you what I have so far.
// some other code, including these functions to generate random numbers:
float nextFloat(float rangeStart, float rangeEnd);
int nextInt(int rangeStart, int rangeEnd);
...
// generates a color somewhere between green and brown
Color stemColor = Color.getHSBColor(nextFloat(0.1, 0.2), nextFloat(0.5, 1), nextFloat(0.2, 0.8));
int stemHeight = nextInt(height/2, 3*height/4);
int stemWidth = nextInt(height/20, height/20 + height/5);
Color flowerColor = ??? // I just couldn't use the same method as above to generate bright colors, but I'm sure it's not too difficult
int flowerRadius = nextInt(Math.min(stemHeight, height - stemHeight)/4, 3*Math.min(stemHeight, height - stemHeight)/4);
I get a logical riddle and I need an efficient algorithm to solve it.
I have large rectangle (box) with size w*h (width*height).
I have also x other rectangles with not size but with fixed proportions.
What is the fastest way to get the x that will let each of the X rectangle the maximum size to be inside the box(large rectangle)?
Example:
The box rectangle size is 150* 50 (width * height) and i have 25 small rectangles.
The fixed proportion of the small rectangle is 3 (if height =5 then width =5*3=15).
Lets call the height of the rectangle x.
I want to find that largest X that will let me to insert all the rectangle into the big rectangle (into the box).
(The small rectangles will be placed in rows and columns, for example 5 columns and 5 rows by the proportion and maximum height)
Does anyone know an efficient algorithm to solve this?
Um what?
Isn't it just (w*h)/75?
Yeah, brackets aren't needed... but isn't that what you want? Or am i totes missing something here?
Where w and h are the dimensions of the big or parent rectangle.
And 75 is 3*25.
I would attempt to solve this problem empirically (solve using backtracking) instead of analytically, i.e. find all possibilities* (I'll explain the *). Essentially we want to place every rectangle starting with as small as that rect can be to its maximum size (max size can be defined by largest the rectangle can be before bumping into the start point of its neighbors or growing to the container master rect). What this means is if we attempt to place every rect in its every possible size, one of those solutions will be the best solution. Also note that this really a one dimentional problem since the rects height and width is bound by a ratio; setting one implicitly sets the other.
* - When I said all possibilities, I really meant most reasonable possibilities. Since we are in floating point space we cannot test ALL possibilities. We can test for finer and finer precision, but will be unable to test all sizes. Due to this we define a step size to iterate through the size of the rects we will try.
const float STEP_SIZE = 0.0001;
float fLastTotalSize = 0;
int main()
{
PlaceRect(myRects.begin(), myRects.end());
}
void PlaceRect(Iterator currentRect, Iterator end)
{
if (currentRect == end)
{
return;
}
float fRectMaxSize = CalculateMaxPossibleRectSize(*currentRect);
// find the number of steps it will take to iterate from the smallest
// rect size to the largest
int nSteps = fRectMaxSize / STEP_SIZE;
for(int i = 0; i < nSteps; ++i)
{
// based on the step index scale the rect size
float fCurrentRectTestSize = i*STEP_SIZE;
currentRect->SetSize(fCurrentRectTestSize);
float fTotalSize = CalculateTotalSizesOfAllRects();
if (fTotalSize > fLastTotalSize)
{
fLastTotalSize = fTotalSize;
SaveRectConfiguration();
}
// Continue placing the rest of the rects assuming the size
// we just set for the current rect
PlaceRect(currentRect + 1, end);
// Once we return we can now reset the current rect size to
// something else and continue testing possibilities
}
}
Based on the step size and the number of rectangles this may run for a very long time, but will find you the empirical solution.
Given a rectangle with width and height, fill it with n squares (n is integer, also given), such that the squares cover as much of the rectangle's area as possible.
The size of a single square should be returned.
Ideas?
The squares do not necessarily have to be oriented the same as the larger rectangle. These sorts of problems are known as packing problems, and finding optimal solutions is notoriously hard.
For an excellent treatment in the case when the larger shape into which the n squares are packed is a square, see Erich Friedman's paper Packing Unit Squares in Squares:
A Survey and New Results
For example, Gödel was the first to publish on this subject. He found that a2+a+3+(a-1)√2 squares can be packed in a square of side a+1+1/√2 by placing a diagonal strip of squares at a 45 degree angle. For example,
And just for fun, I highly recommend you check out Erich's Packing Center.
I know that question was a long time ago, but here is what i though :
you have n square
you have a rectangle
you want to know the size of the square to fill the rectangle
for example :
rectangle of 1280*720 filled with 100 squares.
The surface is 1280*720=921600
1 square should have the surface of 921600/100 = 9216
so the square size is sqrt(9216)=96
In the end it would just be a function that return the result of this :
sqrt((width*height)/n)
Assuming that all the squares are aligned and they are the same size, you can find this by binary searching on the side length of the square:
import math
def best_square(w, h, n):
hi, lo = float(max(w, h)), 0.0
while abs(hi - lo) > 0.000001:
mid = (lo+hi)/2.0
midval = math.floor(w / mid) * math.floor(h / mid)
if midval >= n:
lo = mid
elif midval < n:
hi = mid
return min(w/math.floor(w/lo), h/math.floor(h/lo))
Here is my solution
The idea is to go on a recursive loop
Suppose u start with square_counter =0
While length and breath:
// find the biggest possible square
Count1 = length/breath // take the floor
Square_counted += count1
New balance length = length - count1* breath
Now square with Max size possible wrt breath
Count2 = breath/length
Square_count += count2
Breath = breath - count* length
I have N scalable square tiles (buttons) that need to be placed inside of fixed sized rectangular surface (toolbox). I would like to present the buttons all at the same size.
How could I solve for the optimal size of the tiles that would provide the largest area of the rectangular surface being covered by tiles.
Let W and H be the width and height of the rectangle.
Let s be the length of the side of a square.
Then the number of squares n(s) that you can fit into the rectangle is floor(W/s)*floor(H/s). You want to find the maximum value of s for which n(s) >= N
If you plot the number of squares against s you will get a piecewise constant function. The discontinuities are at the values W/i and H/j, where i and j run through the positive integers.
You want to find the smallest i for which n(W/i) >= N, and similarly the smallest j for which n(H/j) >= N. Call these smallest values i_min and j_min. Then the largest of W/i_min and H/j_min is the s that you want.
I.e. s_max = max(W/i_min,H/j_min)
To find i_min and j_min, just do a brute force search: for each, start from 1, test, and increment.
In the event that N is very large, it may be distasteful to search the i's and j's starting from 1 (although it is hard to imagine that there will be any noticeable difference in performance). In this case, we can estimate the starting values as follows. First, a ballpark estimate of the area of a tile is W*H/N, corresponding to a side of sqrt(W*H/N). If W/i <= sqrt(W*H/N), then i >= ceil(W*sqrt(N/(W*H))), similarly j >= ceil(H*sqrt(N/(W*H)))
So, rather than start the loops at i=1 and j=1, we can start them at i = ceil(sqrt(N*W/H)) and j = ceil(sqrt(N*H/W))). And OP suggests that round works better than ceil -- at worst an extra iteration.
Here's the algorithm spelled out in C++:
#include <math.h>
#include <algorithm>
// find optimal (largest) tile size for which
// at least N tiles fit in WxH rectangle
double optimal_size (double W, double H, int N)
{
int i_min, j_min ; // minimum values for which you get at least N tiles
for (int i=round(sqrt(N*W/H)) ; ; i++) {
if (i*floor(H*i/W) >= N) {
i_min = i ;
break ;
}
}
for (int j=round(sqrt(N*H/W)) ; ; j++) {
if (floor(W*j/H)*j >= N) {
j_min = j ;
break ;
}
}
return std::max (W/i_min, H/j_min) ;
}
The above is written for clarity. The code can be tightened up considerably as follows:
double optimal_size (double W, double H, int N)
{
int i,j ;
for (i = round(sqrt(N*W/H)) ; i*floor(H*i/W) < N ; i++){}
for (j = round(sqrt(N*H/W)) ; floor(W*j/H)*j < N ; j++){}
return std::max (W/i, H/j) ;
}
I believe this can be solved as a constrained minimisation problem, which requires some basic calculus. .
Definitions:
a, l -> rectangle sides
k -> number of squares
s -> side of the squares
You have to minimise the function:
f[s]:= a * l/s^2 - k
subject to the constraints:
IntegerPart[a/s] IntegerPart[l/s] - k >= 0
s > 0
I programed a little Mathematica function to do the trick
f[a_, l_, k_] := NMinimize[{a l/s^2 - k ,
IntegerPart[a/s] IntegerPart[l/s] - k >= 0,
s > 0},
{s}]
Easy to read since the equations are the same as above.
Using this function I made up a table for allocating 6 squares
as far as I can see, the results are correct.
As I said, you may use a standard calculus package for your environment, or you may also develop your own minimisation algorithm and programs. Ring the bell if you decide for the last option and I'll provide a few good pointers.
HTH!
Edit
Just for fun I made a plot with the results.
And for 31 tiles:
Edit 2: Characteristic Parameters
The problem has three characteristic parameters:
The Resulting Size of the tiles
The Number of Tiles
The ratio l/a of the enclosing rectangle
Perhaps the last one may result somewhat surprising, but it is easy to understand: if you have a problem with a 7x5 rectangle and 6 tiles to place, looking in the above table, the size of the squares will be 2.33. Now, if you have a 70x50 rectangle, obviously the resulting tiles will be 23.33, scaling isometrically with the problem.
So, we can take those three parameters and construct a 3D plot of their relationship, and eventually match the curve with some function easier to calculate (using least squares for example or computing iso-value regions).
Anyway, the resulting scaled plot is:
I realize this is an old thread but I recently solved this problem in a way that I think is efficient and always gives the correct answer. It is designed to maintain a given aspect ratio. If you wish for the children(buttons in this case) to be square just use an aspect ratio of 1. I am currently using this algorithm in a few places and it works great.
double VerticalScale; // for the vertical scalar: uses the lowbound number of columns
double HorizontalScale;// horizontal scalar: uses the highbound number of columns
double numColumns; // the exact number of columns that would maximize area
double highNumRows; // number of rows calculated using the upper bound columns
double lowNumRows; // number of rows calculated using the lower bound columns
double lowBoundColumns; // floor value of the estimated number of columns found
double highBoundColumns; // ceiling value of the the estimated number of columns found
Size rectangleSize = new Size(); // rectangle size will be used as a default value that is the exact aspect ratio desired.
//
// Aspect Ratio = h / w
// where h is the height of the child and w is the width
//
// the numerator will be the aspect ratio and the denominator will always be one
// if you want it to be square just use an aspect ratio of 1
rectangleSize.Width = desiredAspectRatio;
rectangleSize.Height = 1;
// estimate of the number of columns useing the formula:
// n * W * h
// columns = SquareRoot( ------------- )
// H * w
//
// Where n is the number of items, W is the width of the parent, H is the height of the parent,
// h is the height of the child, and w is the width of the child
numColumns = Math.Sqrt( (numRectangles * rectangleSize.Height * parentSize.Width) / (parentSize.Height * rectangleSize.Width) );
lowBoundColumns = Math.Floor(numColumns);
highBoundColumns = Math.Ceiling(numColumns);
// The number of rows is determined by finding the floor of the number of children divided by the columns
lowNumRows = Math.Ceiling(numRectangles / lowBoundColumns);
highNumRows = Math.Ceiling(numRectangles / highBoundColumns);
// Vertical Scale is what you multiply the vertical size of the child to find the expected area if you were to find
// the size of the rectangle by maximizing by rows
//
// H
// Vertical Scale = ----------
// R * h
//
// Where H is the height of the parent, R is the number of rows, and h is the height of the child
//
VerticalScale = parentSize.Height / lowNumRows * rectangleSize.Height;
//Horizontal Scale is what you multiply the horizintale size of the child to find the expected area if you were to find
// the size of the rectangle by maximizing by columns
//
// W
// Vertical Scale = ----------
// c * w
//
//Where W is the width of the parent, c is the number of columns, and w is the width of the child
HorizontalScale = parentSize.Width / (highBoundColumns * rectangleSize.Width);
// The Max areas are what is used to determine if we should maximize over rows or columns
// The areas are found by multiplying the scale by the appropriate height or width and finding the area after the scale
//
// Horizontal Area = Sh * w * ( (Sh * w) / A )
//
// where Sh is the horizontal scale, w is the width of the child, and A is the aspect ratio of the child
//
double MaxHorizontalArea = (HorizontalScale * rectangleSize.Width) * ((HorizontalScale * rectangleSize.Width) / desiredAspectRatio);
//
//
// Vertical Area = Sv * h * (Sv * h) * A
// Where Sv isthe vertical scale, h is the height of the child, and A is the aspect ratio of the child
//
double MaxVerticalArea = (VerticalScale * rectangleSize.Height) * ((VerticalScale * rectangleSize.Height) * desiredAspectRatio);
if (MaxHorizontalArea >= MaxVerticalArea ) // the horizontal are is greater than the max area then we maximize by columns
{
// the width is determined by dividing the parent's width by the estimated number of columns
// this calculation will work for NEARLY all of the horizontal cases with only a few exceptions
newSize.Width = parentSize.Width / highBoundColumns; // we use highBoundColumns because that's what is used for the Horizontal
newSize.Height = newSize.Width / desiredAspectRatio; // A = w/h or h= w/A
// In the cases that is doesnt work it is because the height of the new items is greater than the
// height of the parents. this only happens when transitioning to putting all the objects into
// only one row
if (newSize.Height * Math.Ceiling(numRectangles / highBoundColumns) > parentSize.Height)
{
//in this case the best solution is usually to maximize by rows instead
double newHeight = parentSize.Height / highNumRows;
double newWidth = newHeight * desiredAspectRatio;
// However this doesn't always work because in one specific case the number of rows is more than actually needed
// and the width of the objects end up being smaller than the size of the parent because we don't have enough
// columns
if (newWidth * numRectangles < parentSize.Width)
{
//When this is the case the best idea is to maximize over columns again but increment the columns by one
//This takes care of it for most cases for when this happens.
newWidth = parentSize.Width / Math.Ceiling(numColumns++);
newHeight = newWidth / desiredAspectRatio;
// in order to make sure the rectangles don't go over bounds we
// increment the number of columns until it is under bounds again.
while (newWidth * numRectangles > parentSize.Width)
{
newWidth = parentSize.Width / Math.Ceiling(numColumns++);
newHeight = newWidth / desiredAspectRatio;
}
// however after doing this it is possible to have the height too small.
// this will only happen if there is one row of objects. so the solution is to make the objects'
// height equal to the height of their parent
if (newHeight > parentSize.Height)
{
newHeight = parentSize.Height;
newWidth = newHeight * desiredAspectRatio;
}
}
// if we have a lot of added items occaisionally the previous checks will come very close to maximizing both columns and rows
// what happens in this case is that neither end up maximized
// because we don't know what set of rows and columns were used to get us to where we are
// we must recalculate them with the current measurements
double currentCols = Math.Floor(parentSize.Width / newWidth);
double currentRows = Math.Ceiling(numRectangles/currentCols);
// now we check and see if neither the rows or columns are maximized
if ( (newWidth * currentCols ) < parentSize.Width && ( newHeight * Math.Ceiling(numRectangles/currentCols) ) < parentSize.Height)
{
// maximize by columns first
newWidth = parentSize.Width / currentCols;
newHeight = newSize.Width / desiredAspectRatio;
// if the columns are over their bounds, then maximized by the columns instead
if (newHeight * Math.Ceiling(numRectangles / currentCols) > parentSize.Height)
{
newHeight = parentSize.Height / currentRows;
newWidth = newHeight * desiredAspectRatio;
}
}
// finally we have the height of the objects as maximized using columns
newSize.Height = newHeight;
newSize.Width = newWidth;
}
}
else
{
//Here we use the vertical scale. We determine the height of the objects based upong
// the estimated number of rows.
// This work for all known cases
newSize.Height = parentSize.Height / lowNumRows;
newSize.Width = newSize.Height * desiredAspectRatio;
}
At the end of the algorithm 'newSize' holds the appropriate size. This is written in C# but it would be fairly easy to port to other languages.
The first, very rough heuristic is to take
s = floor( sqrt( (X x Y) / N) )
where s is the button-side-length, X and Y are the width and height of the toolbox, and N is the number of buttons.
In this case, s will be the MAXIMUM possible side-length. It is not necessarily possible to map this set of buttons onto the toolbar, however.
Imagine a toolbar that is 20 units by 1 unit with 5 buttons. The heuristic will give you a side length of 2 (area of 4), with a total covering area of 20. However, half of each button will be outside of the toolbar.
I would take an iterative approach here.
I would check if it is possible to fit all button in a single row.
If not, check if it is possible to fit in two rows, and so on.
Say W is the smaller side of the toolbox.
H is the other side.
For each iteration, I would check for the best and worst possible cases, in that order. Best case means, say it is the nth iteration, would try a size of W/n X W/n sized buttons. If h value is enough then we are done. If not, the worst case is to try (W/(n+1))+1 X (W/(n+1))+1 sized buttons. If it is possible to fit all buttons, then i would try a bisection method between W/n and (W/(n+1))+1. If not iteration continues at n+1.
Let n(s) be the number of squares that can fit and s their side. Let W, H be the sides of the rectangle to fill. Then n(s) = floor(W/s)* floor(H/s). This is a monotonically decreasing function in s and also piecewise constant, so you can perform a slight modification of binary search to find the smallest s such that n(s) >= N but n(s+eps) < N. You start with an upper and lower bound on s u = min(W, H) and l = floor(min(W,H)/N) then compute t = (u + l) / 2. If n(t) >= N
then l = min(W/floor(W/t), H/floor(H/t)) otherwise u = max(W/floor(W/t), H/floor(H/t)). Stop when u and l stay the same in consecutive iterations.
So it's like binary search, but you exploit the fact that the function is piecewise constant and the change points are when W or H are an exact multiple of s. Nice little problem, thanks for proposing it.
We know that any optimal solution (there may be two) will fill the rectangle either horizontally or vertically. If you found an optimal solution that did not fill the rectangle in one dimension, you could always increase the scale of the tiles to fill one dimension.
Now, any solution that maximizes the surface covered will have an aspect ratio close to the aspect ratio of the rectangle. The aspect ratio of the solution is vertical tile count/horizontal tile count (and the aspect ratio of the rectangle is Y/X).
You can simplify the problem by forcing Y>=X; in other words, if X>Y, transpose the rectangle. This allows you to only think about aspect ratios >= 1, as long as you remember to transpose the solution back.
Once you've calculated the aspect ratio, you want to find solutions to the problem of V/H ~= Y/X, where V is the vertical tile count and H is the horizontal tile count. You will find up to three solutions: the closest V/H to Y/X and V+1, V-1. At that point, just calculate the coverage based on the scale using V and H and take the maximum (there could be more than one).