矩形内未知数字的最大平方大小

时间:2009-05-15 14:23:46

标签: math tiles max-size

如果我有一组可以是任意数字的瓷砖(正方形)并且它们要填充未知尺寸的容器(矩形),我如何计算出瓷砖的最大尺寸而不会有任何重叠。

因此,如果我有2个瓷砖并且矩形是100 * 100那么最大瓷砖尺寸是50 * 50.如果这个尺寸的rectanlgle有3或4个瓷砖,这也是瓷砖的最大尺寸,这只是在这个例子中恰好是一个正方形。

如果矩形是100 * 30且我有2个瓷砖,则正方形的最大尺寸为30 * 30,如果我有4个瓷砖,则最大尺寸为25 * 25.

如何通过遍历每个可能的组合来编程,而不是占用处理器,从而以编程方式执行此操作。


我试着总结一下, 我有一个:

矩形/边界框,我需要尽可能多地填充,而不会重叠瓷砖。

我知道矩形的高度和宽度(但这可以在运行时更改)。

我有X个牌(这可以在运行时改变),这些是正方形。

每个图块都不应重叠,每个图块的最大尺寸是多少。它们的大小都一样。

10 个答案:

答案 0 :(得分:5)

这是一个包装问题。很难找到最佳解决方案。请参阅示例Packing N squares in a square

您可以通过将总面积除以平方数来计算(乐观)上限:sqrt(width*height/n)

答案 1 :(得分:5)

概念上:

  • 以1平方
  • 开头
  • 对于每个额外的广场,如果您没有 到目前为止,您的网格框中的房间缩小了 现有的盒子就足够了 额外的行或列的空间。

伪代码:给定M x N矩形以填充K平方

// initial candidate grid within the rectangle
h=1
w=1
maxsquares=1
size=min(M,N) //size of the squares
while K > maxsquares
  if M/(h+1) >= N/(w+1)
    h=h+1
  else
    w=w+1
  endif
  maxsquares=h*w
  size=min(M/h,N/w)
done
print size

对于非常大的K,可能有更快的方法来跳转到答案,但我想不出来。如果你知道M和N是整数,那么可能会有更快的方法。

答案 2 :(得分:4)

这是一个没有循环的O(1)解决方案。

使用矩形的纵横比(高度/宽度),您可以对x和y方向上的平铺数量进行初步猜测。这给出了瓦片总数的上限和下限:在xy和(x + 1)(y + 1)之间。

基于这些界限,有三种可能性:

  1. 下限是正确的。计算将导致xy tile的最大tileSize。
  2. 上限是正确的。计算将产生(x + 1)(y + 1)个tile的最大tileSize
  3. 正确答案位于上限和下限之间。求解方程以确定x和y的正确值,然后计算将产生正确数量的tile的最大tileSize
int GetTileSize(int width, int height, int tileCount)
{
    // quick bailout for invalid input
    if (width*height < tileCount) { return 0; }

    // come up with an initial guess
    double aspect = (double)height/width;
    double xf = sqrtf(tileCount/aspect);
    double yf = xf*aspect;
    int x = max(1.0, floor(xf));
    int y = max(1.0, floor(yf));
    int x_size = floor((double)width/x);
    int y_size = floor((double)height/y);
    int tileSize = min(x_size, y_size);

    // test our guess:
    x = floor((double)width/tileSize);
    y = floor((double)height/tileSize);
    if (x*y < tileCount) // we guessed too high
    {
        if (((x+1)*y < tileCount) && (x*(y+1) < tileCount))
        {
            // case 2: the upper bound is correct
            //         compute the tileSize that will
            //         result in (x+1)*(y+1) tiles
            x_size = floor((double)width/(x+1));
            y_size = floor((double)height/(y+1));
            tileSize = min(x_size, y_size);
        }
        else
        {
            // case 3: solve an equation to determine
            //         the final x and y dimensions
            //         and then compute the tileSize
            //         that results in those dimensions
            int test_x = ceil((double)tileCount/y);
            int test_y = ceil((double)tileCount/x);
            x_size = min(floor((double)width/test_x), floor((double)height/y));
            y_size = min(floor((double)width/x), floor((double)height/test_y));
            tileSize = max(x_size, y_size);
        }
    }

    return tileSize;
}

我已使用以下代码测试了此函数的所有整数宽度,高度和tileCounts在1到1000之间:

for (width = 1 to 1000)
{
    for (height = 1 to 1000)
    {
        for (tileCount = 1 to 1000)
        {
            tileSize = GetTileSize(width, height, tileCount);

            // verify that increasing the tileSize by one
            // will result in too few tiles
            x = floor((double)width/(tileSize+1));
            y = floor((double)height/(tileSize+1));
            assert(x*y < tileCount);

            // verify that the computed tileSize actually
            // results in the correct tileCount
            if (tileSize > 0)
            {
                x = floor((double)width/tileSize);
                y = floor((double)height/tileSize);
                assert(x*y >= tileCount);
            }
        }
    }
}

答案 3 :(得分:3)

我设法提出了一个'相对'的最佳解决方案。部分基于Zac的伪代码答案。

        //total number of tiles
        var tile_count : Number = numberOfSlides;
        //height of rectangle
        var b : Number = unscaledHeight;
        //width of rectanlge
        var a : Number = unscaledWidth;

        //divide the area but the number of tiles to get the max area a tile could cover
        //this optimal size for a tile will more often than not make the tiles overlap, but
        //a tile can never be bigger than this size
        var maxSize : Number = Math.sqrt((b * a) / tile_count);
        //find the number of whole tiles that can fit into the height
        var numberOfPossibleWholeTilesH : Number = Math.floor(b / maxSize);
        //find the number of whole tiles that can fit into the width
        var numberOfPossibleWholeTilesW : Number = Math.floor(a / maxSize);
        //works out how many whole tiles this configuration can hold
        var total : Number = numberOfPossibleWholeTilesH * numberOfPossibleWholeTilesW;

        //if the number of number of whole tiles that the max size tile ends up with is less than the require number of 
        //tiles, make the maxSize smaller and recaluate
        while(total < tile_count){
            maxSize--;
            numberOfPossibleWholeTilesH = Math.floor(b / maxSize);
            numberOfPossibleWholeTilesW = Math.floor(a / maxSize);
            total = numberOfPossibleWholeTilesH * numberOfPossibleWholeTilesW;
        }

        return maxSize;

这样做是为了计算出矩形的总面积,然后除以所需数量的瓷砖。由于每个瓷砖都是正方形,我可以对此进行SQRT,以便获得最佳瓷砖的最大尺寸。

使用这个最佳尺寸我然后检查我可以在宽度和宽度上放入多少个WHOLE瓦片。高度。将这些加在一起,如果它小于所需的瓷砖数量,那么我会减小最佳尺寸并再次进行检查,直到所有瓷砖都适合矩形。

我可以通过做一些事情来进一步优化这一点,例如每次减少最小尺寸-2的-1,然后如果所有的瓷砖适合增加1,只是为了确保我没有错过有效尺寸。或者我可以跳回超过-2,说-10然后如果他们所有的瓷砖适合增加5,那么如果不适合减少-2等等,直到我得到最佳拟合。

查看http://kennethsutherland.com/flex/stackover/SlideSorterOK.html我的示例。 感谢所有各种信息。

答案 4 :(得分:1)

以下函数计算给定信息的最大尺寸图块。

如果它是用Python编写的,这使你很难理解,请在评论中告诉我,我会尝试用其他语言来完成。

import math
from __future__ import division

def max_tile_size(tile_count, rect_size):
    """
    Determine the maximum sized tile possible.

    Keyword arguments:
    tile_count -- Number of tiles to fit
    rect_size -- 2-tuple of rectangle size as (width, height)
    """

    # If the rectangle is taller than it is wide, reverse its dimensions
    if rect_size[0] < rect_size[1]:
        rect_size = rect_size[1], rect_size[0]

    # Rectangle aspect ratio
    rect_ar = rect_size[0] / rect_size[1]

    # tiles_max_height is the square root of tile_count, rounded up
    tiles_max_height = math.ceil(math.sqrt(tile_count))

    best_tile_size = 0

    # i in the range [1, tile_max_height], inclusive
    for i in range(1, tiles_max_height + 1):

        # tiles_used is the arrangement of tiles (width, height)
        tiles_used = math.ceil(tile_count / i), i

        # tiles_ar is the aspect ratio of this arrangement
        tiles_ar = tiles_used[0] / tiles_used[1]

        # Calculate the size of each tile
        # Tile pattern is flatter than rectangle
        if tile_ar > rect_ar:
            tile_size = rect_size[0] / tiles_used[0]
        # Tile pattern is skinnier than rectangle
        else:
            tile_size = rect_size[1] / tiles_used[1]

        # Check if this is the best answer so far
        if tile_size > best_tile_size:
            best_tile_size = tile_size

    return best_tile_size

print max_tile_size(4, (100, 100))

该算法可以松散地描述如下

  • 如果矩形高于宽度,请将其翻转,使其比较宽。
  • 计算 s ,向上舍入的平铺数量的平方根。 (在代码中命名为tiles_max_height。)
  • i 从1到 s 的循环:
    • 构建一个正方形的网格,其中多个图块 / i 正方形宽, i 正方形高。 (将所有内容都整理好。这会“填充”丢失的瓷砖,例如当您的瓷砖总数为3时,使用2个瓷砖2个瓷砖。)
    • 使此网格尽可能大。 (使用纵横比计算。)确定一个图块的大小。
    • 使用该尺寸,确定瓷砖覆盖的总面积。
    • 检查这是否是迄今为止最好的总面积;如果是,请存储方形尺寸
  • 返回方形尺寸

这可能是此处列出的更快算法之一,因为它为 n 切片计算O(sqrt( n ))中的最佳平方大小。


<强>更新

在进一步考虑时,基于上述解决方案,该问题具有更简单的解决方案。假设你有30个瓷砖。您可能的平铺安排很容易计算:

  • 30 x 1(宽高比30.0000)
  • 15 x 2(宽高比7.5000)
  • 10 x 3(宽高比3.3333)
  • 8 x 4(宽高比2.0000)
  • 6 x 5(宽高比1.2000)
  • 6 x 6(纵横比1.0000)

假设你的矩形是100 x 60.你的矩形的纵横比是1.6667。这是在1.2和2之间。现在,您只需要计算8 x 4和6 x 5排列的磁贴大小。

第一步仍然在技术上采用O(sqrt( n )),所以这个更新的方法并不比第一次尝试快得多。


评论主题的一些更新

/*
Changes made:

tiles_used -> tiles_used_columns, tiles_used_rows
    (it was originally a 2-tuple in the form (colums, rows))
*/

/* Determine the maximum sized tile possible. */
private function wesleyGetTileSize() : Number {
    var tile_count : Number = slideCount.value;
    var b : Number = heightOfBox.value;
    var a : Number = widthOfBox.value;
    var ratio : Number;    

    // // If the rectangle is taller than it is wide, reverse its dimensions    

    if (a < b) {
        b = widthOfBox.value;
        a = heightOfBox.value;
    } 

    // Rectangle aspect ratio   
    ratio = a / b;    

    // tiles_max_height is the square root of tile_count, rounded up    
    var tiles_max_height : Number = Math.ceil(Math.sqrt(tile_count))    
    var tiles_used_columns : Number;
    var tiles_used_rows : Number;
    var tiles_ar : Number;
    var tile_size : Number;

    var best_tile_size : Number = 0;    

    // i in the range [1, tile_max_height], inclusive   
    for(var i: Number = 1; i <= tiles_max_height + 1; i++) {       
        // tiles_used is the arrangement of tiles (width, height)        
        tiles_used_columns = Math.ceil(tile_count / i);   
        tiles_used_rows = i;

        // tiles_ar is the aspect ratio of this arrangement        
        tiles_ar = tiles_used_columns / tiles_used_rows;        

        // Calculate the size of each tile        
        // Tile pattern is flatter than rectangle       
        if (tiles_ar > ratio){           
            tile_size = a / tiles_used[0]   ;
        }    
        // Tile pattern is skinnier than rectangle        
        else {            
            tile_size = b / tiles_used[1];
        }        
        // Check if this is the best answer so far        
        if (tile_size > best_tile_size){           
            best_tile_size = tile_size;
        }   
    }

    returnedSize.text = String(best_tile_size);
    return best_tile_size;
}

答案 5 :(得分:0)

您能详细说明如何定义填充吗?如果我按照您的描述(大的if),似乎您描述的许多情况实际上并没有填充矩形。例如,你说100 * 100矩形中的2个方格将是50 * 50。如果我正确理解您的配置,它们将被放置在此矩形的“对角线”上。但是那个矩形也会有两个尺寸为50 * 50的“间隙”。这不是我认为的“填充”矩形。我会把问题说成2个(大小相等的正方形)的最大可能尺寸,其边界框为100 * 100(假设每个正方形必须与至少一个其他正方形接触?)。

这里的关键点是你的矩形似乎是一个边界框而没有填充。

另外,你能为这个计算编写一个功能界面吗?考虑到边界框的尺寸,你是否需要为n个可能的正方形做这个?

答案 6 :(得分:0)

给定值:

N - number of tiles
a, b - sides of the rectangle
可以使用此函数计算图块的

侧:

def maxSize(n: Int, a: Int, b: Int) = {
  var l = 0
  for (i <- 1 until a.min(b)) { // 
    val newL = (a.min(b) / i).min( (a.max(b) * i)/n )
    if (l < newL && ((a.min(b)/newL) * (a.max(b)/newL) >= n )  )
      l = newL
  }
  return l
}

我认为你不会制作小于1x1的瓷砖,无论1的尺度是什么

首先从0开始:

l = 0

然后你从1到K列的瓦片迭代

K = min(a, b)

对于每次迭代,使用此公式

计算图块的新最大边
val newL = ( a.min(b) / i ).min( (a.max(b) * i)/n )

此公式采用这两个值中的较小值:

1. min(a, b)/i -- maximum length of a tile if there are i columns in the smaller side of the rectangle
2. (i * max(a, b))/n -- maximum length of a tile if there are i columns and n tiles in the bigger side of the rectangle

如果候选newL大于初始值l并且可以放入正方形而没有重叠的最大可能瓦片数量大于或等于瓦片数量n则

l = newL

在最后返回l

答案 7 :(得分:0)

我认为方块不能旋转。如果允许你旋转它们,我很确定问题非常严重。

所以我们从左上角开始用正方形填充矩形。然后我们将方块放在该方块的右边,直到我们到达矩形的右侧,然后我们对下一行做同样的操作,直到我们到达底部。这就像在纸上书写文字一样。

观察到永远不会出现右侧和底部留有空间的情况。如果两个方向都有空间,那么我们仍然可以增加正方形的大小。

假设我们已经知道应该在第一行放置10个方格,并且这完全符合宽度。然后边长为width/10。因此,我们可以在第一列中放置m = height/sidelength个方块。这个公式可以说我们可以在第一列放置2.33个方格。不可能放置0.33的正方形,我们只能放置2个正方形。真正的公式是m = floor(height/sidelength)

一个不是很快(但比尝试每个组合快一点)算法是尝试首先在第一行/列上放置1个方格,然后看看我们是否可以在矩形中放置足够的方块。如果它不起作用,我们在第一行/列等上尝试2个方格,直到我们可以拟合你想要的瓷砖数量。

我认为如果允许你在O(1)中进行算术,那么存在一个O(1)算法,但到目前为止我还没有想到它。

这是此算法的Ruby版本。如果矩形不是很薄,则该算法为O(sqrt(瓦片数))。

def squareside(height, width, tiles)
  n = 0
  while true
    n += 1
    # case 1: the squares fill the height of the rectangle perfectly with n squares
    side = height/n
    m = (width/side).floor # the number of squares that fill the width
    # we're done if we can place enough squares this way
    return side if n*m >= tiles
    # case 2: the squares fill the width of the rectangle perfectly with n squares
    side = width/n
    m = (height/side).floor
    return side if n*m >= tiles
  end
end

您也可以对此算法使用二进制搜索。在那种情况下,它是O(log(瓦片数))。

答案 8 :(得分:-1)

x = max(rectHeight/numberOfSquares, rectangleLength/numberOfSquares)

if x <= retangleHeight && x <= rectangleLength then
  squareSideLength = x
else
  squareSideLength = min(rectangleHeight, rectangleLength)

答案 9 :(得分:-2)

将长边除以瓷砖数量。使用较短的一面作为瓷砖尺寸。普雷斯托!瓷砖数量。

Rectagle = 200 x 10
Each tile is 10 x 10 (length of shorter side)
200/10 = 20 (number of tiles needed)