|
-
May 12th, 2009, 02:38 PM
#1
Thread Starter
Member
Rectangle "best fit" optimisation
I need suggestions 
Imagine a cardboard box 1200 x 1000. You have to place smaller boxes (325 x 225) inside the larger one. The smaller boxes can only be rotated in multiples of 90 degrees. How many small boxes can be fitted into the large one?
The absolute "theoretical" maximum number of boxes would be (1200 x 1000) \ (325 x 225).
So how do I go about programming the remaining logic to ascertain the "best fit"
-
May 12th, 2009, 11:15 PM
#2
Re: Rectangle "best fit" optimisation
The complexity is in the rotation. Sometimes you might get a better fit by rotating one or several rows. I can't think of an easy way to characterize it, though, apart from some brute force if the actual problem size allows it. I'll think on it...
The time you enjoy wasting is not wasted time.
Bertrand Russell
<- Remember to rate posts you find helpful.
-
May 13th, 2009, 03:21 AM
#3
Thread Starter
Member
Re: Rectangle "best fit" optimisation
Brute force may be possible, because there will be a fairly limited number of combinations.
I've written the first step (simple row and column checking), but I'd still like suggestions.
Code:
Private sMsg As String = ""
Private Success As Boolean = False
Private Success_Step As Integer = 0
Private OuterBox As Size
Private PieceToPlace As Size
Private MaxTheoretical As Integer = 0
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdCalculate.Click
OuterBox = New Size(1200, 1000)
PieceToPlace = New Size(396, 141)
'PieceToPlace = New Size(ToInt32(nudWidth.Value), ToInt32(nudHeight.Value))
Dim TimeTaken As New Stopwatch
TimeTaken.Start()
MaxTheoretical = (OuterBox.Width * OuterBox.Height) \ (PieceToPlace.Width * PieceToPlace.Height)
Perform_Steps()
TimeTaken.Stop()
sMsg = ""
sMsg &= String.Format("Assuming outer box size = {1} x {2}{0}{0}", Environment.NewLine, OuterBox.Width, OuterBox.Height)
sMsg &= String.Format("Maximum theoretical pieces = {1}{0}{0}", Environment.NewLine, MaxTheoretical)
If Success = True Then
sMsg &= String.Format("**** SUCCESS !! ****{0}Step = {2}, time taken = {3}ms{0}{0}", Environment.NewLine, Success, Success_Step, TimeTaken.ElapsedMilliseconds)
Else
sMsg &= String.Format("Success = {1}, time taken = {3}ms{0}{0}", Environment.NewLine, Success, Success_Step, TimeTaken.ElapsedMilliseconds)
End If
sMsg &= String.Format("STEP 1{0}", Environment.NewLine)
sMsg &= String.Format("{1} \ {2} = {3} @ {4:00.00}%{0}", Environment.NewLine, OuterBox.Width, PieceToPlace.Width, PieceWidthIntoWidth, ((PieceWidthIntoWidth * PieceToPlace.Width) / OuterBox.Width) * 100)
sMsg &= String.Format("{1} \ {2} = {3} @ {4:00.00}%{0}", Environment.NewLine, OuterBox.Width, PieceToPlace.Height, PieceHeightIntoWidth, ((PieceHeightIntoWidth * PieceToPlace.Height) / OuterBox.Width) * 100)
sMsg &= String.Format("{1} \ {2} = {3} @ {4:00.00}%{0}", Environment.NewLine, OuterBox.Height, PieceToPlace.Width, PieceWidthIntoHeight, ((PieceWidthIntoHeight * PieceToPlace.Width) / OuterBox.Height) * 100)
sMsg &= String.Format("{1} \ {2} = {3} @ {4:00.00}%{0}", Environment.NewLine, OuterBox.Height, PieceToPlace.Height, PieceHeightIntoHeight, ((PieceHeightIntoHeight * PieceToPlace.Height) / OuterBox.Height) * 100)
If Success_Step = 1 Then
txtOutput.Text = sMsg
Exit Sub
End If
'no result so far ...
txtOutput.Text = sMsg
Exit Sub
End Sub
Private Sub Perform_Steps()
Success = False
Success_Step = 0
Step_1()
If Success Then Exit Sub
End Sub
#Region "_ Step 1 _"
Private PieceWidthIntoWidth As Integer = 0
Private PieceHeightIntoWidth As Integer = 0
Private PieceWidthIntoHeight As Integer = 0
Private PieceHeightIntoHeight As Integer = 0
Private Sub Step_1()
PieceWidthIntoWidth = OuterBox.Width \ PieceToPlace.Width
PieceHeightIntoWidth = OuterBox.Width \ PieceToPlace.Height
PieceWidthIntoHeight = OuterBox.Height \ PieceToPlace.Width
PieceHeightIntoHeight = OuterBox.Height \ PieceToPlace.Height
If (PieceWidthIntoWidth * PieceHeightIntoHeight) = MaxTheoretical OrElse (PieceHeightIntoWidth * PieceWidthIntoHeight) = MaxTheoretical Then
Success = True
Success_Step = 1
End If
End Sub
#End Region
-
May 13th, 2009, 10:31 AM
#4
Re: Rectangle "best fit" optimisation
Here's my approach.
I've called W and H the width and height of the rectangular area and a the widht and b the height of a small rectangle ("brick") that's to be packed inside.
Let:
NP = number of rows with the bricks in portrait orientation
NL = number of rows with the bricks in landscape orientation
(see figures)
nP = INT(W/a) is the number of bricks per row (portrait)
nL = INT(W/b) is the number of bricks per row (landscape)
Then the total number of bricks is, at least:
Total = NP*nP + NL*nL
where these restrictions apply:
NP <= INT(H/a)
NL <= INT(H/b)
b*NP + a*NL <= H
Now, in some cases, after packing the last rows in landscape mode there could be some space left at one side (left or right) to place a few moore bricks in portrait orientation (see figure). This is the case if both these 2 conditions are met:
W - b*nL >= a
H - b*NP >= b
If you call:
k1 = W - b * nL
k2 = H - b * NP
then k1*k2 is the number of extra bricks.
VB Code:
'Rectangle width and height Const W As Single = 1200 Const H As Single = 800 'Brick width (a) and height (b) Const a As Single = 37 Const b As Single = 83 Private Sub main() Dim nRowsPort As Integer, nRowsLand As Integer Dim nBricksPerRowPort As Integer, nBricksPerRowLand As Integer Dim nRowsPortMax As Integer, nRowsLandMax As Integer 'Height of all the rows (portrait plus landscape) Dim TotalH As Integer 'Total number of bricks Dim TotalN As Integer 'Total bricks with possible extras Dim GrandTotalN As Integer Dim tmp As Integer, i As Integer, j As Integer Dim dummy1 As Single, dummy2 As Single nBricksPerRowPort = Int(W / a) nBricksPerRowLand = Int(W / b) 'Maximum values for nRowsPort and nRowsLand: nRowsPortMax = Int(H / b) nRowsLandMax = Int(H / a) 'Try all integer combinations of nRowsPort and nRowsLand 'with the restrictions: 'nRowsPort <= nRowsPortMax 'nRowsLand <= nRowsLandMax 'b*nRowsPort + a*nRowsLand <= H TotalN = 0 For i = 1 To nRowsPortMax For j = 1 To nRowsLandMax TotalH = b * i + a * j If TotalH <= H Then tmp = i * nBricksPerRowPort + j * nBricksPerRowLand If tmp > TotalN Then TotalN = tmp nRowsPort = i nRowsLand = j End If End If Next Next 'Check if some space is left to fit in a few more dummy1 = W - b * nBricksPerRowLand dummy2 = H - b * nRowsPort If dummy1 >= a And dummy2 >= b Then 'Number of extra rows of bricks i = Int(dummy2 / b) 'Number of extra bricks per row j = Int(dummy1 / a) 'Number of extra bricks i = i * j Else i = 0 End If 'Grand total GrandTotalN = TotalN + i Debug.Print "Total bricks: " & CStr(TotalN) Debug.Print "Extra bricks: " & CStr(i) Debug.Print "Grand total bricks: " & CStr(GrandTotalN) Debug.Print "Rows (portrait): " & CStr(nRowsPort) Debug.Print "Rows (landscape): " & CStr(nRowsLand) End Sub
Last edited by krtxmrtz; May 13th, 2009 at 10:35 AM.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
-
May 13th, 2009, 01:00 PM
#5
Re: Rectangle "best fit" optimisation
Isn't this basically the knapsack problem?
Maybe you can implement some kind of simple genetic algorithm, I think those are suited for problems like this (although I think they are better suited for less 'square' problems where you 1200*1000 grid is actually some arbitrary shape and your blocks are also arbitrarily shaped.
-
May 13th, 2009, 04:46 PM
#6
Thread Starter
Member
Re: Rectangle "best fit" optimisation
 Originally Posted by krtxmrtz
Here's my approach.
Thanks for that Seems like your pallets are smaller than ours 
One point. If I take out my first step and just rely on yours, it wont find any solutions with only portrait or landscape rows. I suggest two lines are changed to :-
Code:
For i = 0 To nRowsPortMax
For j = 0 To nRowsLandMax
 Originally Posted by NickThissen
Isn't this basically the knapsack problem?
Yes Nick, in a way it probably is I'll look at writing some form of algorithm - but krtxmrtz's solution is working quite well at the moment
-
May 14th, 2009, 02:47 AM
#7
Re: Rectangle "best fit" optimisation
 Originally Posted by InertiaM
...
One point. If I take out my first step and just rely on yours, it wont find any solutions with only portrait or landscape rows. I suggest two lines are changed to :-
Code:
For i = 0 To nRowsPortMax
For j = 0 To nRowsLandMax
...
Good suggestion, as a matter of fact I didn't think much about possible improvements. Probably the method -and the code- can be further refined.
Lottery is a tax on people who are bad at maths
If only mosquitoes sucked fat instead of blood...
To do is to be (Descartes). To be is to do (Sartre). To be do be do (Sinatra)
-
May 19th, 2009, 07:21 AM
#8
Re: Rectangle "best fit" optimisation
I used this code in EXCEL
Code:
Public Sub CalculateMaxBrickNumer()
Dim BoxWidth As Single
Dim BoxHeight As Single
Dim BrickHeight As Single
Dim BrickWidth As Single
Dim i As Integer
Dim j As Integer
Dim MaxNumber As Integer
Dim Number As Integer
MaxNumber = 0
Dim NumberRowsHeightHeight As Integer
Dim NumberRowsHeightWidth As Integer
Dim NumberColumnsWidthWidth As Integer
Dim NumberColumnsWidthHeight As Integer
Dim MaxNumberRowsHeightHeight As Integer
Dim MaxNumberRowsHeightWidth As Integer
Dim MaxNumberColumnsWidthWidth As Integer
Dim MaxNumberColumnsWidthHeight As Integer
'The values are set on the activesheet in the named cells
BoxWidth = ThisWorkbook.ActiveSheet.Range("BoxBreite").Value
BoxHeight = ThisWorkbook.ActiveSheet.Range("BoxLänge").Value
BrickWidth = ThisWorkbook.ActiveSheet.Range("BrickBreite").Value
BrickHeight = ThisWorkbook.ActiveSheet.Range("BrickLänge").Value
'The bricks will be placed for all possible numbers of columns in the direction BoxWidth to BrickWidth,
'the rest of the useable Width will be used for placement of Bricks in the other direction.
For i = 0 To Int(BoxWidth / BrickWidth)
'i Columns of bricks are placed Width to Width
NumberColumnsWidthWidth = i
'calculate the possible number of Columns for Bricks in the other direction in the remaining Width
NumberColumnsWidthHeight = Int((BoxWidth - i * BrickWidth) / BrickHeight)
'how many Rows are possible direction BoxHeight to BrickHeight
NumberRowsHeightHeight = Int(BoxHeight / BrickHeight)
'how many Rows are possible direction BoxHeight to BrickWidth
NumberRowsHeightWidth = Int(BoxHeight / BrickWidth)
'Number of Bricks in Total
Number = NumberColumnsWidthWidth * NumberRowsHeightHeight + NumberColumnsWidthHeight * NumberRowsHeightWidth
If Number > MaxNumber Then
MaxNumber = Number
MaxNumberRowsHeightHeight = NumberRowsHeightHeight
MaxNumberRowsHeightWidth = NumberRowsHeightWidth
MaxNumberColumnsWidthWidth = NumberColumnsWidthWidth
MaxNumberColumnsWidthHeight = NumberColumnsWidthHeight
End If
'Reset the Numbers
NumberRowsHeightHeight = 0
NumberRowsHeightWidth = 0
NumberColumnsWidthWidth = 0
NumberColumnsWidthHeight = 0
Next i
'Reverse the values and calculate again
BoxHeight = ThisWorkbook.ActiveSheet.Range("BoxBreite").Value
BoxWidth = ThisWorkbook.ActiveSheet.Range("BoxLänge").Value
BrickHeight = ThisWorkbook.ActiveSheet.Range("BrickBreite").Value
BrickWidth = ThisWorkbook.ActiveSheet.Range("BrickLänge").Value
'The bricks will be placed for all possible numbers of columns in the direction BoxWidth to BrickWidth,
'the rest of the useable Width will be used for placement of Bricks in the other direction.
For i = 0 To Int(BoxWidth / BrickWidth)
'i Columns of bricks are placed Width to Width
NumberColumnsWidthWidth = i
'calculate the possible number of Columns for Bricks in the other direction in the remaining Width
NumberColumnsWidthHeight = Int((BoxWidth - i * BrickWidth) / BrickHeight)
'how many Rows are possible direction BoxHeight to BrickHeight
NumberRowsHeightHeight = Int(BoxHeight / BrickHeight)
'how many Rows are possible direction BoxHeight to BrickWidth
NumberRowsHeightWidth = Int(BoxHeight / BrickWidth)
'Number of Bricks in Total
Number = NumberColumnsWidthWidth * NumberRowsHeightHeight + NumberColumnsWidthHeight * NumberRowsHeightWidth
If Number > MaxNumber Then
MaxNumber = Number
MaxNumberRowsHeightHeight = NumberRowsHeightHeight
MaxNumberRowsHeightWidth = NumberRowsHeightWidth
MaxNumberColumnsWidthWidth = NumberColumnsWidthWidth
MaxNumberColumnsWidthHeight = NumberColumnsWidthHeight
End If
'Reset the Numbers
NumberRowsHeightHeight = 0
NumberRowsHeightWidth = 0
NumberColumnsWidthWidth = 0
NumberColumnsWidthHeight = 0
Next i
'Show the result on the Sheet
ThisWorkbook.ActiveSheet.Cells(30, 1).Value = "MaxNumber: " & CStr(MaxNumber)
ThisWorkbook.ActiveSheet.Cells(31, 1).Value = "RowsHeightHeight: " & CStr(MaxNumberRowsHeightHeight)
ThisWorkbook.ActiveSheet.Cells(32, 1).Value = "RowsHeightWidth: " & CStr(MaxNumberRowsHeightWidth)
ThisWorkbook.ActiveSheet.Cells(33, 1).Value = "ColumnsWidthWidth: " & CStr(MaxNumberColumnsWidthWidth)
ThisWorkbook.ActiveSheet.Cells(34, 1).Value = "ColumnsWidthHeight: " & CStr(MaxNumberColumnsWidthHeight)
End Sub
Last edited by opus; May 19th, 2009 at 09:14 AM.
Reason: Optimized Code
You're welcome to rate this post!
If your problem is solved, please use the Mark thread as resolved button
Wait, I'm too old to hurry!
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|