矩形项目的优化网格

时间:2010-03-19 10:02:32

标签: algorithm math layout mathematical-optimization

我有N个矩形项目,长宽比为Aitem(X:Y) 我有一个长宽比为Aview的矩形显示区域

项目应按表格式排列(即r行,c列)。

理想的网格行x列是什么,因此单个项目最大? (行* colums> = N,当然 - 即可能有“未使用的”网格位置)。

一个简单的算法可以迭代rows = 1..N,计算所需的列数,并保持行/列对的最大项。

我想知道是否存在非迭代算法(例如,对于Aitem = Aview = 1,行/列可以用sqrt(N)近似)。

4 个答案:

答案 0 :(得分:2)

注意:我无法理解Frédéric的答案,所以我自己解决了这个问题并提出了看似相同的解决方案。我想我也可以解释一下我做了什么,以防它有用。

首先,我将视图的宽高比标准化为项目的宽高比。 (我假设你不想旋转物品。)

a = (view_width/view_height) / (item_width/item_height)

现在用正方形包装宽度/高度比a的矩形相当于用项目打包视图。理想的情况是我们的网格(现在是方格)完全填充矩形,这将给我们

a = c/r

其中rc是行数和列数:

N = r*c

乘以/除以这两个方程给我们

N*a = c^2              N/a = r^2
c = sqrt(N*a)          r = sqrt(N/a)

如果网格是完美的,rc将是整数,但如果没有,则必须尝试Frédéric提到的三个选项,并保留r*c最小的那个选项。仍然超过N

  • floor(r), ceil(c)
  • ceil(r), floor(c)
  • ceil(r), ceil(c)

答案 1 :(得分:1)

您的解决方案可以轻松改进以处理通用案例:

如果我们(临时)忘记了需要整数个行和列,我们有

rows * columns = N

x = aitem * y

aview = rows * x = rows * aitem * y

1 =列* y =(N /行)*(aview / [aitem * rows])= N * aview /(aitem *rows²)

因此rows = sqrt(N * aview / aitem)和columns = N / rows = sqrt(N * aitem / aview)

然后ceil(行)和ceil(列)是一个解决方案,而floor(行)和floor(列)太小而不能一起解决(如果行和列不是整数)。这留下了3种可能的解决方案:

  • floor(rows)ceil(columns)
  • ceil(行)楼层(列)
  • ceil(rows)ceil(columns)

编辑以更正公式。第一个结果是错误的(见评论)

答案 2 :(得分:0)

好问题。如果您的视图的尺寸为A x B(固定)且您的项目的尺寸为x b(变量,最大化),那么您需要:

trunc(A / a) * trunc(B / b) >= N

我不知道如何解决这个问题 - 截断是棘手的部分,因为它是非线性的。

答案 3 :(得分:0)

我无法获得该问题的答案对我有用,但是我找到了一个Neptilo中类似问题的简洁实现。但是,它不适用于矩形,仅适用于正方形。因此,我运用了mckeed中的想法来对矩形进行归一化,然后遵循正方形的算法。

结果是fitToContainer()函数。给它提供适合ncontainerWidthcontainerHeight以及原始itemWidthitemHeight的矩形数量。如果商品没有原始的宽度和高度,请使用itemWidthitemHeight指定所需的商品比例。

例如,fitToContainer(10, 1920, 1080, 16, 9)产生{nrows: 4, ncols: 3, itemWidth: 480, itemHeight: 270},因此四列和三行480 x 270(像素或任何单位)。

要在1920x1080的相同示例区域中容纳10个正方形,您可以调用fitToContainer(10, 1920, 1080, 1, 1),得到{nrows: 2, ncols: 5, itemWidth: 384, itemHeight: 384}

function fitToContainer(n, containerWidth, containerHeight, itemWidth, itemHeight) {
    // We're not necessarily dealing with squares but rectangles (itemWidth x itemHeight),
    // temporarily compensate the containerWidth to handle as rectangles
    containerWidth = containerWidth * itemHeight / itemWidth;
    // Compute number of rows and columns, and cell size
    var ratio = containerWidth / containerHeight;
    var ncols_float = Math.sqrt(n * ratio);
    var nrows_float = n / ncols_float;

    // Find best option filling the whole height
    var nrows1 = Math.ceil(nrows_float);
    var ncols1 = Math.ceil(n / nrows1);
    while (nrows1 * ratio < ncols1) {
        nrows1++;
        ncols1 = Math.ceil(n / nrows1);
    }
    var cell_size1 = containerHeight / nrows1;

    // Find best option filling the whole width
    var ncols2 = Math.ceil(ncols_float);
    var nrows2 = Math.ceil(n / ncols2);
    while (ncols2 < nrows2 * ratio) {
        ncols2++;
        nrows2 = Math.ceil(n / ncols2);
    }
    var cell_size2 = containerWidth / ncols2;

    // Find the best values
    var nrows, ncols, cell_size;
    if (cell_size1 < cell_size2) {
        nrows = nrows2;
        ncols = ncols2;
        cell_size = cell_size2;
    } else {
        nrows = nrows1;
        ncols = ncols1;
        cell_size = cell_size1;
    }

    // Undo compensation on width, to make squares into desired ratio
    itemWidth = cell_size * itemWidth / itemHeight;
    itemHeight = cell_size;
    return { nrows: nrows, ncols: ncols, itemWidth: itemWidth, itemHeight: itemHeight }
}