Results 1 to 8 of 8

Thread: Rectangle "best fit" optimisation

  1. #1

    Thread Starter
    Member
    Join Date
    Mar 2008
    Location
    East Kent, UK
    Posts
    34

    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"

  2. #2
    Only Slightly Obsessive jemidiah's Avatar
    Join Date
    Apr 2002
    Posts
    2,431

    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.

  3. #3

    Thread Starter
    Member
    Join Date
    Mar 2008
    Location
    East Kent, UK
    Posts
    34

    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

  4. #4
    vbuggy krtxmrtz's Avatar
    Join Date
    May 2002
    Location
    In a probability cloud
    Posts
    5,573

    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:
    1. 'Rectangle width and height
    2. Const W As Single = 1200
    3. Const H As Single = 800
    4. 'Brick width (a) and height (b)
    5. Const a As Single = 37
    6. Const b As Single = 83
    7. Private Sub main()
    8.     Dim nRowsPort As Integer, nRowsLand As Integer
    9.     Dim nBricksPerRowPort As Integer, nBricksPerRowLand As Integer
    10.     Dim nRowsPortMax As Integer, nRowsLandMax As Integer
    11.     'Height of all the rows (portrait plus landscape)
    12.     Dim TotalH As Integer
    13.     'Total number of bricks
    14.     Dim TotalN As Integer
    15.     'Total bricks with possible extras
    16.     Dim GrandTotalN As Integer
    17.     Dim tmp As Integer, i As Integer, j As Integer
    18.     Dim dummy1 As Single, dummy2 As Single
    19.    
    20.     nBricksPerRowPort = Int(W / a)
    21.     nBricksPerRowLand = Int(W / b)
    22.    
    23.     'Maximum values for nRowsPort and nRowsLand:
    24.     nRowsPortMax = Int(H / b)
    25.     nRowsLandMax = Int(H / a)
    26.     'Try all integer combinations of nRowsPort and nRowsLand
    27.     'with the restrictions:
    28.     'nRowsPort <= nRowsPortMax
    29.     'nRowsLand <= nRowsLandMax
    30.     'b*nRowsPort + a*nRowsLand <= H
    31.     TotalN = 0
    32.     For i = 1 To nRowsPortMax
    33.         For j = 1 To nRowsLandMax
    34.             TotalH = b * i + a * j
    35.             If TotalH <= H Then
    36.                 tmp = i * nBricksPerRowPort + j * nBricksPerRowLand
    37.                 If tmp > TotalN Then
    38.                     TotalN = tmp
    39.                     nRowsPort = i
    40.                     nRowsLand = j
    41.                 End If
    42.             End If
    43.         Next
    44.     Next
    45.     'Check if some space is left to fit in a few more
    46.     dummy1 = W - b * nBricksPerRowLand
    47.     dummy2 = H - b * nRowsPort
    48.     If dummy1 >= a And dummy2 >= b Then
    49.         'Number of extra rows of bricks
    50.         i = Int(dummy2 / b)
    51.         'Number of extra bricks per row
    52.         j = Int(dummy1 / a)
    53.         'Number of extra bricks
    54.         i = i * j
    55.     Else
    56.         i = 0
    57.     End If
    58.    
    59.     'Grand total
    60.     GrandTotalN = TotalN + i
    61.    
    62.     Debug.Print "Total bricks: " & CStr(TotalN)
    63.     Debug.Print "Extra bricks: " & CStr(i)
    64.     Debug.Print "Grand total bricks: " & CStr(GrandTotalN)
    65.     Debug.Print "Rows (portrait): " & CStr(nRowsPort)
    66.     Debug.Print "Rows (landscape): " & CStr(nRowsLand)
    67. End Sub
    Attached Images Attached Images   
    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)

  5. #5
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    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.

  6. #6

    Thread Starter
    Member
    Join Date
    Mar 2008
    Location
    East Kent, UK
    Posts
    34

    Re: Rectangle "best fit" optimisation

    Quote Originally Posted by krtxmrtz View Post
    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

    Quote Originally Posted by NickThissen View Post
    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

  7. #7
    vbuggy krtxmrtz's Avatar
    Join Date
    May 2002
    Location
    In a probability cloud
    Posts
    5,573

    Re: Rectangle "best fit" optimisation

    Quote Originally Posted by InertiaM View Post
    ...
    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)

  8. #8
    I don't do your homework! opus's Avatar
    Join Date
    Jun 2000
    Location
    Good Old Europe
    Posts
    3,863

    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&#228;nge").Value
        BrickWidth = ThisWorkbook.ActiveSheet.Range("BrickBreite").Value
        BrickHeight = ThisWorkbook.ActiveSheet.Range("BrickL&#228;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&#228;nge").Value
        BrickHeight = ThisWorkbook.ActiveSheet.Range("BrickBreite").Value
        BrickWidth = ThisWorkbook.ActiveSheet.Range("BrickL&#228;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
  •  



Click Here to Expand Forum to Full Width