【问题标题】:Space utilization algorithm or Mesh creation algorithm空间利用算法或网格创建算法
【发布时间】:2015-01-23 07:01:43
【问题描述】:

嘿,我们正面临空间利用问题,或者我不清楚应该给问题起什么名字。

基本上是网格问题。

我试图用一张图片来解释我的问题。

问题陈述有点像下面。

  1. 带有对角线的盒子是一个必须以最佳比例分布的物品,例如它应该适合所有可用的容器。
  2. 容器以不同的颜色显示。
  3. 现在所有容器都将变成矩形。
  4. 所有容器必须以纵向模式或横向模式放置。

容器和物品都可以测量宽度和高度,对于程序来说它们基本上都是像素。

根据其他成员的cmets,SpektreLasse V. Karlsen这里是相同的澄清

  1. 这是一个 2D 排列
  2. 是的,我们可以重新排列容器以实现最佳模式。
  3. 项目的任何部分都不应在空格中。项目必须是任何容器的一部分。
  4. 项目可以与容器重叠,并且高度和宽度可以因容器而异。并且 Item 的高度宽度也可以变化,但形状始终保持矩形。
  5. 如果项目位于左上角,则更可取。
  6. 是的,它有点像装箱算法,但该算法的唯一问题是,在装箱中,物品更多,容器是一,在我们的例子中,物品是一,容器更多。所以基本上这是一个分布问题。
  7. 问题实际上是我们有容器的大小并且需要放置容器以便我们可以创建那个矩形。

程序应该给出以下输出

  1. 容器的位置
  2. 容器内的部分物品。
  3. 和排列模式。

【问题讨论】:

  • 只是一些问题以便更好地理解:它是 2D 吗? 2.您想重新排列容器(而不仅仅是单个容器)以实现:1. 项目的任何部分都不在空白区域,2. 项目与所有容器重叠 3. 首选的容器覆盖范围是多少? (图像项目可以在每个方向上移动一点,所以最好的位置是什么,或者没关系?)...顺便说一句,看起来像bin packing问题的一些特殊情况将使用多少个容器?
  • 定义“适合”。你的意思是你想要一个矩形的最大表面积,可以放在其他容器覆盖的区域内吗?它必须重叠所有这些还是表面积是最大的问题?您是否有一些需要保持的宽高比?
  • 容器矩形是否已经放置,或者问题实际上是你有容器的大小并且需要放置容器以便你可以创建那个矩形?您需要在此处指定更多条件和允许的操作。
  • 我修改了问题并满足了你们的疑问。您以正确的方式理解了我的问题。
  • 所以项目不需要重叠所有的容器?它适合其中一些就足够了......我将从重新排列容器开始以满足内接矩形区域的大小。找出哪个轴最难实现并从中构建...

标签: algorithm geometry computational-geometry bin-packing


【解决方案1】:

这里有一些简单但不理想但很容易作为起点的东西

  • 基于我的 cmets
  • 利用通用容器大小 480 像素

算法:

  1. 旋转所有容器(箱)以获得 480 高度
  2. 旋转降序后按宽度对 bin 进行排序
  3. 需要 ceil(1080/480)=3 行 480 像素的 bin
  4. 使用最宽的箱子来填充所有线条,但不要超过 1920 像素

    • 它们已排序,因此请使用第一个
    • 所有使用过的都标记为已使用
    • 仅使用未使用的垃圾箱
  5. 将剩余的箱子排列成行(到最短的行)

    • 所以带上未使用的垃圾箱
    • 确定哪条线最短
    • 如果最短的线已经是 1920 像素或更大,则停止
    • 如果不将垃圾箱移动到该行并将其标记为已使用

C++ 源代码(丑陋的静态分配但简单且没有使用 lib):

//---------------------------------------------------------------------------
struct _rec { int x,y,xs,ys,_used; };
_rec bin[128],item; int bins=0;
//---------------------------------------------------------------------------
void bin_generate(int n)    // generate problem
    {
    int i;
    Randomize();
    item.x=0;
    item.y=0;
    item.xs=1920;
    item.ys=1080;
    for (bins=0;bins<n;bins++)
        {
        bin[bins].x=0;
        bin[bins].y=0;
        i=Random(2);
             if (i==0) { bin[bins].xs=320; bin[bins].ys=480; }
        else if (i==1) { bin[bins].xs=480; bin[bins].ys=800; }
        else i=i;
//      if (i==2) { bin[bins].xs=1920; bin[bins].ys=1080; }
        }
    }
//---------------------------------------------------------------------------
void bin_solve()            // try to solve problem
    {
    int i,e,n,x,y,x0[128],y0[128],common=480;
    _rec *r,*s,t;

    // rotate bins to ys=480
    for (r=bin,i=0;i<bins;i++,r++) if (r->xs==common) { x=r->xs; r->xs=r->ys; r->ys=x; }
    // sort bins by xs desc
    for (e=1;e;) for (e=0,r=bin,s=r+1,i=1;i<bins;i++,r++,s++) if (r->xs<s->xs) { t=*r; *r=*s; *s=t; e=1; }
    // prepare lines needed ... n is num of lines, _rest is one common side height line is needed to add
    n=item.ys/common; if (item.ys%common) n++; item.x=0; item.y=0;
    for (i=0;i<n;i++) { x0[i]=0; y0[i]=common*i; }
    for (r=bin,i=0;i<bins;i++,r++) r->_used=0;
    // arrange wide bins to lines
    for (e=0;e<n;e++)
     for (r=bin,i=0;i<bins;i++,r++)
      if (!r->_used)
       if (x0[e]+r->xs<=item.xs)
        {
        r->x=x0[e];
        r->y=y0[e];
        r->_used=1;
        x0[e]+=r->xs;
        if (x0[e]>=item.xs) break;
        }
    // arrange rest bins to lines (goes to the shortest line)
     for (r=bin,i=0;i<bins;i++,r++)
      if (!r->_used)
        {
        // find shortest line
        for (e=0,x=0;x<n;x++) if (x0[e]>x0[x]) e=x;
        // stop if shortest line is already wide enough
        if (x0[e]>=item.xs) break;
        // fit the bin in it
        r->x=x0[e];
        r->y=y0[e];
        r->_used=1;
        x0[e]+=r->xs;
        }
    // arrange the unused rest below
    for (x=0,y=n*common+40,r=bin,i=0;i<bins;i++,r++) if (!r->_used) { r->x=x; r->y=y; x+=r->xs; }
    }
//---------------------------------------------------------------------------

用法:

  • bin_generate(7); // 生成 n 个随机设备到 bin[bins] 矩形数组
  • bin_solve(); // 尝试解决问题...只需重新排列 bin[bins]
  • 这不是最佳选择,但稍作调整就足够了
  • 例如,最后 2 行需要 600px 的高度,因此如果您有该尺寸或更大的设备,您可以使用它们将最后 2 行填充为 1 行...
  • 如果不是,那么可能一些图表或树方法会更好(由于容器数量少)

[Edit1] 通用尺寸

当您没有保证固定的通用容器大小时,您必须改为计算它...

//---------------------------------------------------------------------------
struct _rec { int x,y,xs,ys,_used;      _rec(){}; _rec(_rec& a){ *this=a; }; ~_rec(){}; _rec* operator = (const _rec *a) { *this=*a; return this; }; /*_rec* operator = (const _rec &a) { ...copy... return this; };*/ };
List<_rec> bin,bintype;
_rec item;
//---------------------------------------------------------------------------
void bin_generate(int n)    // generate problem
    {
    int i;
    _rec r;
    Randomize();
    // target resolution
    item.x=0; item.xs=1920;
    item.y=0; item.ys=1080;
    // all used device sizes in portrait start orientation
    bintype.num=0; r.x=0; r.y=0; r._used=0;
    r.xs= 320; r.ys= 480; bintype.add(r);
    r.xs= 480; r.ys= 800; bintype.add(r);
    r.xs= 540; r.ys= 960; bintype.add(r);
//  r.xs=1080; r.ys=1920; bintype.add(r);
    // create test case
    bin.num=0; for (i=0;i<n;i++) bin.add(bintype[Random(bintype.num)]);
    }
//---------------------------------------------------------------------------
void bin_solve()            // try to solve problem
    {
    int i,j,k,e,x,y;
    _rec *r,s;
    List<int> hsiz,hcnt;    // histogram of sizes
    List< List<int> > lin;  // line of bins with common size
    // compute histogram of sizes
    hsiz.num=0; hcnt.num=0;
    for (r=bin.dat,i=0;i<bin.num;i++,r++)
        {
        x=r->xs; for (j=0;j<hsiz.num;j++) if (x==hsiz[j]) { hcnt[j]++; j=-1; break; } if (j>=0) { hsiz.add(x); hcnt.add(1); }
        x=r->ys; for (j=0;j<hsiz.num;j++) if (x==hsiz[j]) { hcnt[j]++; j=-1; break; } if (j>=0) { hsiz.add(x); hcnt.add(1); }
        }
    // sort histogram by cnt desc (most occurent sizes are first)
    for (e=1;e;) for (e=0,j=0,i=1;i<hsiz.num;i++,j++) if (hcnt[j]<hcnt[i])
        {
        x=hsiz[i]; hsiz[i]=hsiz[j]; hsiz[j]=x;
        x=hcnt[i]; hcnt[i]=hcnt[j]; hcnt[j]=x; e=1;
        }
    // create lin[][]; with ys as common size (separate/rotate bins with common sizes from histogram)
    lin.num=0;
    for (r=bin.dat,i=0;i<bin.num;i++,r++) r->_used=0;
    for (i=0;i<hsiz.num;i++)
        {
        lin.add(); lin[i].num=0; x=hsiz[i];
        for (r=bin.dat,j=0;j<bin.num;j++,r++)
            {
            if ((!r->_used)&&(x==r->xs)) { lin[i].add(j); r->_used=1; y=r->xs; r->xs=r->ys; r->ys=y; }
            if ((!r->_used)&&(x==r->ys)) { lin[i].add(j); r->_used=1; }
            }
        }
    for (i=0;i<lin.num;i++) if (!lin[i].num) { lin.del(i); i--; }
    // sort lin[][] by xs desc (widest bins are first)
    for (i=0;i<lin.num;i++)
     for (e=1;e;) for (e=0,k=0,j=1;j<lin[i].num;j++,k++)
      if (bin[lin[i][k]].xs<bin[lin[i][j]].xs)
        { s=bin[lin[i][j]]; bin[lin[i][j]]=bin[lin[i][k]]; bin[lin[i][k]]=s; e=1; }
    // arrange lines to visually check previous code (debug) ... and also compute the total line length (width)
    for (y=item.ys+600,i=0;i<lin.num;i++,y+=r->ys) for (x=0,j=0;j<lin[i].num;j++) { r=&bin[lin[i][j]]; r->x=x; r->y=y; x+=r->xs; }
    for (i=0;i<lin.num;i++)
        {
        j=lin[i][lin[i].num-1];                         // last bin in line
        hsiz[i]=bin[j].x+bin[j].xs;                     // total width
        hcnt[i]=bin[j].ys;                              // line height
        }
    // now compute solution
    for (r=bin.dat,i=0;i<bin.num;i++,r++) r->_used=0;   // reset usage first
    for (y=0,k=1,i=0;i<lin.num;i++)                     // process lines with common size
     while(hsiz[i]>=item.xs)                            // stop if line shorter then needed
        {
        x=0;
        // arrange wide bins to line
        for (j=0;j<lin[i].num;j++)
            {
            r=&bin[lin[i][j]];
            if ((!r->_used)&&(x+r->xs<=item.xs))
                {
                r->x=x; hsiz[i]-=x; x+=r->xs;
                r->y=y; r->_used=k;
                if (x>=item.xs) break;
                }
            }
        // arrange short bins to finish line
        if (x<item.xs)
         for (j=lin[i].num-1;j>=0;j--)
            {
            r=&bin[lin[i][j]];
            if (!r->_used)
                {
                r->x=x; hsiz[i]-=x; x+=r->xs;
                r->y=y; r->_used=k;
                if (x>=item.xs) break;
                }
            }
        // remove unfinished line
        if (x<item.xs)
            {
            for (j=0;j<lin[i].num;j++)
                {
                r=&bin[lin[i][j]];
                if (r->_used==k)
                    {
                    r->x=0; r->y=0;
                    r->_used=0;
                    hsiz[i]+=r->xs;
                    }
                }
            break;
            }
        // next line
        y+=hcnt[i];
        if (y>=item.ys) break;  // solution found already?
        }
    // rotate unused rest to have ys>=as needed but as wide as can be to form last line
    e=item.ys-y; x=0;
    if (e>0) for (r=bin.dat,i=0;i<bin.num;i++,r++)
     if (!r->_used)
        {
        if ((r->xs<e)&&(r->ys<e)) continue; // skip too small bins
        if (r->xs<r->ys) { j=r->xs; r->xs=r->ys; r->ys=j; }
        if (r->ys<    e) { j=r->xs; r->xs=r->ys; r->ys=j; }
        r->x=x; x+=r->xs;
        r->y=y; r->_used=1;
        }
    }
//---------------------------------------------------------------------------
  • 它与以前几乎相同,但在计算容器大小的解决方案直方图之前
  • 选择最常出现的并形成兼容的垃圾箱(容器)组
  • 然后应用算法...
  • 我添加了动态数组模板 List&lt;&gt; 的用法,因为在静态分配上我会在写这篇文章之前发疯...
  • List&lt;int&gt; x; 等同于 int x[];
  • x.numx[] 内的项目数
  • x.add() 将新项目添加到 x[] 的末尾
  • x.add(q) 将新项目 = q 添加到 x[] 的末尾
  • x.del(i) 从 x[] 中删除第 i 个项目 ...索引从零开始
  • 所以改写成你曾经使用过的东西......
  • List&lt; List&lt;int&gt; &gt; y; 是二维数组 y[][] ...
  • 最后从未使用的 bin 中形成最后一行 ...
  • 这既不可靠也不安全,但它大部分都可以工作(它需要一些调整,但我懒得做)
  • 解决方案还取决于输入集的顺序,因此如果您稍微调整一下,您可以为相同的输入集找到更多解决方案...(如果某些常见尺寸具有相同的计数)

【讨论】:

  • 看起来很有潜力的东西,我今天一定要试试这个。如果这解决了我的问题以及有任何疑问,会通知您。非常感谢
  • 同时我们不能假设 480 是一个常见的容器大小。它也可以是 540(w) x 960(h)。
  • @HardikTrivedi 你应该创建一些测试场景,因为设备数量少很难创建有效的随机设备集,所以分享一个真实的输入案例是个好主意
  • @spektre 嘿,我还在研究你的方法,你能告诉我什么 for (y=item.ys+600,i=0;iys) 意味着 ???为什么我们保留 600 以供添加?
  • @HardikTrivedi 只是将排序后的组放在项目下方作为行来检查它上面的分组和排序代码是否对解决方案无关紧要......(这是从一开始就剩下的在解决方案尚未编码时进行编码)现在唯一使用的是计算总行/组长度以查看是否有足够的设备在项目内形成一条线......我的解决方案是x=0,y=0以避免一些@ 987654341@s ...所以在我的测试用例中,项目下方 600px 的空间足够大,不会与解决方案重叠
猜你喜欢
  • 1970-01-01
  • 2015-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-04
相关资源
最近更新 更多