Q译者注Q略Q?/span>
Q译者注Q略Q?/span>
Q译者注Q略Q?/span>
q有几个单的Ҏ(gu)讲述如何打包多个矩ŞC个封闭的矩Ş中:(x)
l 你可以将所有矩形在水^方向上一字排开Q就像这P(x)
q很单也很快Qƈ且可以在所有矩形高度一致时表现的很好?/span>
l 或者你可以在水qx向上排列Q就像这P(x)
q依旧简单快Pq且当所有矩形宽度一致时表现良好?/span>
但是Q这些方案在q些矩阵拥有不同的宽度和高度时留下大量被费的空间。这很烦人。所以接下来我们关注在打包不同宽度和高度矩形时这U浪Ҏ(gu)化Q当然也要尽可能的快和简单?/span>
打包多个矩Ş到尽可能的闭矩Ş的基本算法在Richard E. Korf的文章有有被描述Q?/span>Optimal Rectangle Packing: Initial Results?/span>
1.Ҏ(gu)高度对矩形进行排序,最高的排第一?/span>
2.闭合矩Ş初始讄为:(x)高度为最高矩形的高度Q宽度无限大?/span>
3.矩形一个个的放入封闭矩形中Q由最高的矩Ş开始,到最矮的矩Şl束。放|每个矩形时可能靠左?/span>如果有多个左边位|,使用最高的那个QIf there are several left most locations, use the highest one Q。比如:(x)
a)
b)
c)
d)
e)
4.让闭合矩形的宽度{于弄好的所有矩形的d度。即Q将闭合矩Ş的右边缘向左UdQ直到接触到最双矩Ş的右边缘。这L(fng)话,闭合矩Ş宽度正合适?/span>
5.你已l将所有矩形放|在闭合矩Ş内了吗?如果q样的话Q?/span>
l 如果你得到的q个闭合矩Ş是目前最的?#8220;成功”的,它保存h?/span>
l 试着宽度减一来获取更的闭合区域?/span>
6.然而,如果你没有全部将矩Ş攄在闭合矩形内Q很昄闭合矩Ş了。如果这L(fng)话,高度加一。(译者注Q就q样重复执行攄矩Ş到闭合矩形内Q直到得C个原始最?jng)_度减一×原始高度不断加一加到能将所有矩形都攑օq个新闭合矩形内Q其实ƈ不是W笨的加一Q往下看Q改q基本算法部分有提到q步具体做法Q请注意Q减宽度加高度实际上意味着我们闭合矩形的范围从矮宽到高瘦。比如,当宽度被减少、高度被增加几次后,你可能会(x)得到如下的序列:(x)
a)
b)
c)
d)
e)
7.如果你得到的闭合矩Ş的总面U(?#215;高)比所有将攑օ其中的矩形的面积之和q要,那这明显不是一个可行的闭合矩Ş。高度增加一直到你得C个可行的闭合矩Ş。然后进入到下一步?/span>
8.如果你获得的q个闭合矩Ş比迄今ؓ(f)止最好的闭合矩Ş要大Q那么对它进行测试ƈ无意义。宽度减一然后重复W七步来保不是了Q否则进入下一步?/span>
9.如果你的新的闭合区域比最宽的矩Ş要窄Q你可以停下了Qƈ且汇报你得到的最佳闭合矩形。这是因为我们这个算法从不会(x)增加闭合矩Ş的宽度,q且如果最宽矩形都放不下,q明显不需要再试q个闭合矩Ş了。(If your new enclosing rectangle is narrower than the widest rectangle, you can stop now, and report the best enclosing rectangle you found so far. This is because the algorithm never increases the width of the enclosing rectangle, and if the widest rectangle won't fit, there is obviously no point in testing the enclosing rectangle.Q?/span>
10.现在你的新闭合矩形既不太,也不太大Q返回第三步ȝ看你是否可以所有矩形放q去?/span>
q个法已经在下载工EMapper中的cMapperOptimalEfficiency的函数Mapping中实C?/span>
E后我们看看如何将q个基本法做的更快些。但是首先让我们看看如何矩形放入闭合区域中Q最主要的是如何记录已放入的矩Ş来防止新加入矩Ş与他们重叠?/span>
上面基本法中的W三步写?#8220;矩形一个个的放入闭合矩形内Q从最高的矩Ş开始,最矮的l束。放|每个矩形时可能的靠左Q如果有好几个左边位|,用最高的那个?#8221;
Z扑ֈ最左最高的位置来放|一个给定宽度和高度矩Ş而不与其他矩形重叠,我们需要用一些数据结构来记录闭合矩Ş中已被占用的区域。ƈ且这个数据结构必L单(q样不容易出错)又能快速找到矩形可以被攄的那个最左最高位|?/span>
可以使用一个二l布?yu)(dng)数l,每个格子代表闭合区域中的一个像素。每个布?yu)(dng)标记这个像素是否被占用q是I闲。然而,当你Z个矩形找I时需要访问很多像素,q个Ҏ(gu)单但效率低下?/span>
另一个方案,可以保存闭合矩Ş中每个矩形的宽和高以?qing)他们的X/Y方向的偏UR这个数据结构比起二l像素数据拥有更的成员变量Q但是在I间_的情况下Q计出q个最左最高的位置使用q个l构既复杂又慢?/span>
我想出的解决Ҏ(gu)是:(x)使用一个动态的二维布尔数组表示占用或空Ԍ但是为每行存储一个高度,每列存储一个宽度,q样列数和行数可以保持在最。这h减少复杂度又降低了需要访问的格子敎ͼ和因此花费的旉Q。下面是d矩Ş时这个方法如何工作的Q白色格子是未被占用Q绿色格子是被占用,暗绿色格子是刚被最后一ơ添加矩形占用的Q:(x)
1.首先Q只有一行和闭合矩Ş一样高Q一列和闭合矩Ş一样宽。这意味着只有一个格子?/span>
2.当添加第一个矩形时Q找出最左最高的格子很轻松,因ؓ(f)q儿只有一个。确保他对于q个矩Ş_大也很简单。所以我们l,q把它放|在格子的左上角?/span>
我们现在有个格子被部分占用,部分没有被占用。然而一个格子只能是一U状态。ؓ(f)了解册个问题,单行分开、单列分开Q这h们就获得了四个格子,q样他们要么被占用,要么没有被占用:(x)
3.该添加第二个矩Ş了。首先检查最左边的列。从上到下遍历这个列中所有的格子直到你找C个空闲的格子。然后检查是否能矩形放在那ѝ?/span>
最左列有一个空闲格Q但是垂直方向上不够放下q个矩Ş。那么在双一列如法炮制的查找?/span>
W二列中Q最高的那个格子是空间的Qƈ且够放|矩形,那么q很单。将矩Ş攑֜那。和W一个格子放|时一P矩Ş相对于格子比较小Q导致一个格子部分被占用Q部分未占用。所以分割该格子的行和列来确保格子又回到不是被占用就是未被占用的状态。注意结果,W一个矩形所占用的格子现在被划分Z了,q没关系的?/span>
4.W三个矩形也走同L(fng)程。然而,因ؓ(f)q个矩Ş有点矮,所以最左列有够的I间来放|他。再一ơ,在这个格子中怺的行列被分割了,来取保所有格子要么是占用要么是未被占用?/span>
5.W四个矩形走同样的流E。如果最左列剩余垂直I间不Q那在他右边试试。最左列最高的那个格子_高来攄矩ŞQ但是不够宽。所以测试右边的列看是不是有_I闲的格子来安放矩Ş。可以看到v始列和他双的列l合h在水qx向上有_的空间安放这个矩形,也就是说q个矩Ş可以用两个格子来攄。再一ơ格子在行列上被分割Q确保格子被占用或非占用?/span>
6.W五个矩形,依旧从左到右的检查列。第二列有一个空闲格Q但是对于该矩Ş在垂直方向上没有_的空间。第三列有两个不相连的空闲格子,但是他俩高度都不够,q且垂直I间上也无法和其他格子结合凑_直空间来攄矩Ş?/span>
在第四列Q有一个空间格子,他既不够高也不够宽来安放矩Ş。所以检查右边列的格子和下面行的格子看是不是有够空闲的格子可以l合来容U矩形。在q种情况下,q证明是可行的。再一ơ,发生了行列分割来保证格子占用或非占用的唯一性?/span>
通过q行下蝲文g中的|站Q你可以看到更多的例子。他也演CZ不是所有矩形都能放|的情况?/span>
扑ֈ可以攄矩Ş的最左最高格子和(g)查周围格子的代码在下载中的Mapper工程的CanvascM可以扑ֈ。二l动态数l在DynamicTwoDimensionalArraycM实现。因Z被经怋用,q个cMؓ(f)了性能被高度优化,其是分割行和列时?/span>
当学?fn)下载的test site中生成的试例子Ӟ下面的改q会(x)很明显。在写改q在下蝲的代码中有的。我在文献中没有扑ֈq些改进的方法,所有你可以先读一下他们:(x)
l 减小闭区域宽度后,增加_的高?/span>
l 攄所有矩形失败后Q增加够的高度
l 通过讄效率的上限获得加?/span>
看下下面那个一轮基本算法生成的闭合矩ŞQ?/span>
Ҏ(gu)基本法Q你应该闭合矩形的宽度减一Q然后试着重新安放所有矩形。然而,如果你简单的宽度减一Q你知道暗绿色矩形一定会(x)出闭合矩Ş的右手边界,对它来说无处容n了就Q所以下ơ尝试安放所有矩形时一定会(x)p|?/span>
另外Q算法说当失败的时候将闭合矩Ş的高度加一。但是,因ؓ(f)暗绿色矩形的高度是十个像素,明显高度加一是不够的。所以基本算法接下来一定会(x)q行十次p|的尝试去安放所有矩形,q很费也没啥必要?/span>
可以通过记录接触到闭合矩形右手边界最高矩形的高度来进行优化。如果你成功安放好所有矩形ƈ闭合矩形的宽度减一后,可以闭合矩形的高度加上出闭合矩Şx的最高矩形的高度。这L(fng)法可以在新的闭合矩Ş内ؓ(f)出x的最高矩形找到新的位|:(x)
看看下面的序列。用该法安放好四个矩形后Q放|第五个矩Ş时失败了?/span>
1 2 3 4 攄矩Şp|
q次p|后,基本法对闭合矩Ş的高度加一Q然后重新放|矩形。但是仅仅加一的话Q前四个矩Şq是安放在一模一L(fng)位置Q最l安攄五个矩Şq是p|。这个算法将?x)l高度加一l箋试Q可能还是这U情况,周而复始。这些毫无意义的试占用了大量的旉?/span>
使用下面两个高度中较?yu)的|来代曉K度加一?/span>
1Q?/span>无法安放的矩形的高度
2Q?/span>可以让闭合矩形对前四个矩形可以进行和之前不同排列的高?#8212;—q样臛_l算法一个机?x)来安放W五个矩?/span>
通过选择两项中较?yu)的一个,你将可能得到更接q最的闭合矩Ş?/span>
出上面W二点中提到的高度ƈ不难Q只要你意识到这个算法首先试着在最左列攄每个矩ŞQ如果失败了查看右边这列再ơ尝试,直到成功。每ơ在列中攄矩Şp|Q那是因为列的底部空闲区域对于矩形来说太矮了——I闲高度不?/span>
拿第二个矩Ş来说Q第一列还?0个像素的I闲高度Q?/span>
也就是说Q如果闭合矩形再高那?/span>30个像素,W二个矩形就可以攑֜最左边那列了?/span>
同样的,W三个矩形要攑֜极左列的高度q差25像素Q在W二行还?5像素。所以如果闭合矩形再?5个像素,W三个矩形放|的׃(x)不同了:(x)
W四个矩形在极左列还?/span>10像素的空闲高度。所以如果闭合矩形如果高10个像素,W四个矩形也可能被放|在W一列?/span>
我们惌增加闭合矩Ş所需最的高度使矩形能排的不同卛_。这里,最的所有列所需I闲高度?0个像素(应用于第四个矩Ş攑֜极左列)?/span>׃W五个矩形高?0个像素放|失败,我们需要将闭合矩Ş的高度增?0像素Q希望在下次试时放|第五个矩ŞQ(Seeing that the fifth rectangle that failed to be placed is higher than 10px, we need to increase the height of the enclosing rectangle by 10px to have any hope of placing the fifth rectangle at the next try: Q?/span>
1 2 3 4 5
q意味着Z防止那么多的p|闭合矩ŞQ算法需要简单的记录下来在列中放|矩形时最的I闲高度差额。当攄矩Şp|的时候,可以用这个最空闲高度差额增加闭合矩形的高度——如果无法攄矩Ş的高度更的话就用他?/span>
q种优化不能保下一ơ尝试就能得到成功的闭合矩Ş。比如,新的高度下,闭合矩Ş可能?x)比目前的最佳闭合矩形要打,q种情况下算法将要开始减他的宽度。这L(fng)话可能会(x)在下ơ尝试时无法安置所有的矩Ş因ؓ(f)降低了宽度。此优化是ؓ(f)了减尝试的ơ数而非一ơ成功?/span>
如果你决定你可以忍受一定水q的I间费Q一U方法是减少计算旉来生闭合矩形,是当达到这个水qx让代码停止l获得更的闭合矩Ş?/span>
另一U方法提高速度是当扑ֈ预设ơ数成功闭合矩Ş后就让算法停止。你可以讄限制Zơ或两次。这很有吸引力,当你发现q种Ҏ(gu)可以让你得到一个够好的结果ƈ且提高了不少速度?/span>
Z让你自己选,在MapperOptimalEfficiencycM提供了第二个构造函敎ͼ通过额外的参数设|这两个选项Qwith additional parameters to set these two cut offs Q?/span>?/span>
下蝲Mapper目中有文章中描q的矩Ş打包器的实现。他l外部提供了一个良好的接口。test side使用了这个接口。ؓ(f)了让你更Ҏ(gu)的学?fn)或者在你的目使用q些代码Q下面提供了接口的描q?/span>
你会(x)发现代码涉及(qing)到的是图片和_而不是矩形和闭矩Ş。这是因Z是打作为实时精는成器目Q还未完成)的一部分实现的?/span>
Q译者注Q略Q?/span>
Q译者注Q略Q?/span>
Q译者注Q略Q?/span>
Q译者注Q略Q?/span>
Q译者注Q略Q?/span>
Q译者注Q略Q?/span>