web-dev-qa-db-ja.com

さまざまなサイズの長方形を可能な限り小さい長方形にかなり最適な方法でパックするには、どのアルゴリズムを使用できますか?

長方形のオブジェクトがたくさんあるので、可能な限り小さなスペースに詰める必要があります(このスペースの大きさは2のべき乗でなければなりません)。

アイテムを可能な限り特定のスペースにパックするさまざまなパッキングアルゴリズムを知っていますが、この場合、そのスペースもどれだけ大きくすべきかを判断するアルゴリズムが必要です。

例えば、私は次の長方形を持っていると言います

  • 128 * 32
  • 128 * 64
  • 64 * 32
  • 64 * 32

それらは128 * 128スペースに詰めることができます

 _________________ 
 | 128 * 32 | 
 | ________________ || 
 | 128 * 64 | 
 | | 
 | | 
 | ________________ | 
 | 64 * 32 | 64 * 32 | 
 | _______ | ________ | 

ただし、160 * 32と64 * 64がある場合は、256 * 128スペースが必要になります

 ________________________________ 
 | 128 * 32 | 64 * 64 | 64 * 32 | 
 | ________________ | | _______ | 
 | 128 * 64 | | 64 * 32 | 
 | | _______ | _______ | 
 | | | 
 | ________________ | ___ | 
 | 160 * 32 | | 
 | ____________________ | ___________ | 

多数の長方形をパックし、コンテナーに必要なサイズ(2のべき乗、各次元で指定された最大サイズ内)を決定できるアルゴリズムはありますか?

270
Fire Lancer

クイックでダーティなファーストパスソリューションは、他に比べるものがない場合の比較として、常に優れたソリューションです。

大から小への貪欲な配置。

パックされた領域に残っている最大の長方形を置きます。どこにも収まらない場合は、パック領域をできるだけ拡張しない場所に置きます。最小の長方形で終わるまで繰り返します。

それはまったく完璧ではありませんが、簡単でニースのベースラインです。元の例を完全にパックし、2番目の場合も同等の回答を提供します。

65
SPWorley

ARCプロジェクトのこのページ を参照してください。ソリューションの調査については、実装の複雑さ/時間と最適性の間にはトレードオフがありますが、選択できるアルゴリズムはさまざまです。

アルゴリズムの抜粋は次のとおりです。

  1. First-Fit Decreeasing Height(FFDH)アルゴリズム
    FFDHは、Rが適合する最初のレベルで次のアイテムR(高さが増加しない)をパックします。 Rに対応できるレベルがない場合、新しいレベルが作成されます。
    FFDHの時間の複雑さ:O(n・log n)。
    近似比:FFDH(I)<=(17/10)・OPT(I)+1; 17/10の漸近境界はきついです。

  2. Next-Fit Decreeasing Height(NFDH)アルゴリズム
    NFDHは、Rが適合する場合、現在のレベルで次のアイテムR(高さが増加しない)をパックします。それ以外の場合、現在のレベルは「クローズ」され、新しいレベルが作成されます。
    時間の複雑さ:O(n・log n)。
    近似比:NFDH(I)<= 2・OPT(I)+1; 2の漸近境界はきついです。

  3. 最高適合高さ減少(BFDH)アルゴリズム
    BFDHは、Rに対応できるレベルのうち、水平方向の残りのスペースが最小であるレベルで、次のアイテムR(高さが増加しない)をパックします。 Rに対応できるレベルがない場合、新しいレベルが作成されます。

  4. 左下(BL)アルゴリズム
    BLは、幅を増加させずにアイテムを最初に注文します。 BLは、次のアイテムを収まる限り底の近くでパックし、その後、パックされたアイテムと重ならないようにできる限り左に近づけます。 BLはレベル指向のパッキングアルゴリズムではないことに注意してください。
    時間の複雑さ:O(n ^ 2)。
    近似比:BL(I)<= 3・OPT(I)。

  5. Bakerのアップダウン(UD)アルゴリズム
    UDは、BLとNFDHの一般化の組み合わせを使用します。ストリップとアイテムの幅は、ストリップが単位幅になるように正規化されます。 UDは、アイテムを非増加幅で順序付けし、アイテムを5つのグループに分割します。各グループの幅は(1/2、1]、(1/3,1/2]、(1/4,1/3 ]、(1/5,1/4]、(0,1/5]。ストリップは、5つの領域R1、…、R5にも分割されます。基本的に、範囲(1/i + 1、1/i]、1 <= i <= 4は、BLによって領域Riにパックされます。BLは、ストリップの右側で上から下に向かって幅が広くなるため、UDは最初にこの利点を利用しますj = 1、···、4(順番に)上から下にRjにアイテムを梱包します。そのようなスペースがない場合、アイテムはBLによってRiに梱包されます。 (一般化された)NFDHアルゴリズムにより、R1、…、R4のスペースにパックされますこれらの領域にスペースがない場合、アイテムはNFDHを使用してR5にパックされます。
    近似比:UD(I)<=(5/4)・OPT(I)+(53/8)H、ここでHはアイテムの最大の高さです。 5/4の漸近境界はきついです。

  6. 逆適合(RF)アルゴリズム
    RFは、ストリップの幅が単位幅になるように、ストリップとアイテムの幅も正規化します。 RFは、最初に幅が1/2より大きいすべてのアイテムをスタックします。残りのアイテムは、増加しない高さでソートされ、1/2を超えるものが到達する高さH0より上にパックされます。その後、RFは次のプロセスを繰り返します。大ざっぱに言えば、RFは、スペースがなくなるまで、高さH0の線に沿って下から順に左から右にアイテムをパックします。次に、合計幅が少なくとも1/2になるまで、アイテムを右から左、上から下にパックします(逆レベルと呼ばれます)。次に、(少なくとも)そのうちの1つが下のアイテムに触れるまで、逆レベルがドロップダウンされます。ドロップダウンは何とか繰り返されます。
    近似比:RF(I)<= 2・OPT(I)。

  7. スタインバーグのアルゴリズム
    Steinbergのアルゴリズム(論文ではMと表記)は、すべてのアイテムをパックするのに必要な高さHの上限を推定し、入力アイテムを幅Wと高さHの長方形にパックできることを証明します。次に、7つの手順(7つの条件)を定義し、それぞれが問題を2つの小さな問題に分割し、再帰的に解決します。扱いやすい問題は、7つの条件のいずれかを満たすことが示されています。
    近似比:M(I) <= 2・OPT(I)。

  8. Split-Fitアルゴリズム(SF)SFは、アイテムを2つのグループ(幅が1/2より大きいL1と最大で1/2のL2)に分割します。 L1のすべてのアイテムは、FFDHによって最初にパックされます。次に、幅が2/3を超えるすべてのアイテムが幅が最大で2/3のアイテムよりも下になるように配置されます。これにより、幅が1/3のスペースの長方形Rが作成されます。その後、L2の残りのアイテムはRに、FFDHを使用してL1でパックされたアイテムの上のスペースにパックされます。 Rで作成されたレベルは、L1のパッキングの上で作成されたレベルより下であると見なされます。
    近似比:SF(I)<=(3/2)・OPT(I)+ 2; 3/2の漸近境界はきついです。

  9. Sleatorのアルゴリズム
    Sleaterのアルゴリズムは4つのステップで構成されています。

    1. 幅が1/2を超えるすべてのアイテムは、ストリップの下部に重ねて詰められます。 h0が結果のパッキングの高さであると仮定します。その後のパッキングはすべてh0より上で行われます。

    2. 残りのアイテムは、高さが増加しない順に並べられます。アイテムのレベルは、高さh0のラインに沿って左から右に(高さの増加しない順で)パックされます。

    3. 次に、真ん中に垂直線を引き、ストリップを2つの等しい半分にカットします(この線は、右半分に部分的に詰められたアイテムをカットする場合があります)。半分の長さの2つの水平線セグメントを描画します。1つは左半分(左ベースラインと呼ばれる)に、もう1つは右半分(右ベースラインと呼ばれる)に渡って、2本の線がアイテムを横切らないようにします。

    4. より低い高さの左または右のベースラインを選択し、次のアイテムが広すぎるまでストリップの対応する半分にアイテムのレベルをパックします。

    新しいベースラインが形成され、すべてのアイテムが梱包されるまで下のベースラインでステップ(4)が繰り返されます。
    時間の複雑さ:O(n・log n)。
    Sleatorのアルゴリズムの近似比は2.5で、厳密です。

84

パッキングの問題 をご覧ください。あなたのものは「2Dビン梱包」に該当すると思います。その解決策やその他の梱包問題から多くを学ぶことができるはずです。

以下も参照してください。 長方形の画像データを正方形のテクスチャにパックする。

19
aib

この問題に関する広範な文献があります。貪欲なヒューリスティックは、コンテナーの左下にある最初の使用可能な位置に、最大領域から最小領域に長方形を配置することです。すべてのアイテムを左下隅に引き下げる重力を考えてください。このグーグルの説明については「シャゼル左下梱包」。

最適なソリューションを得るために、最先端の技術は数秒で20以上の長方形を詰め込むことができます。 Huangには algorithm があり、これは最小の囲みバウンディングボックスを見つける問題と、長方形のセットが特定のサイズのバウンディングボックスに収まるかどうかを決定する問題を分離します。あなたは彼のプログラムに長方形のセットを与え、それらをパックするのに必要な最小の囲みボックスを教えてくれます。

あなたの場合、外側のループは、可能な限り小さい境界ボックスから上に向かって反復する必要があります(幅と高さは2の累乗で連続的に増加します)。これらの境界ボックスごとに、長方形のパッキングを見つけることができるかどうかをテストします。最適なソリューションであることが保証される最初の「はい」の回答まで、「いいえ」の回答がたくさんあります。

特定のサイズのバウンディングボックスに「はい」または「いいえ」と答えるアルゴリズムの内部ループについては、Huang参照を検索し、アルゴリズムを実装するだけです。彼は基本的なアルゴリズムに加えて多くの最適化を行っていますが、基本的な肉とジャガイモだけが本当に必要です。回転を処理したいので、検索中のすべての分岐点で、両方の回転が解決にならない場合は、単に回転とバックトラックの両方を試してください。

17
Eric

これは NP困難な問題 であると確信しています。そのため、最適なソリューションを得るには、可能な組み合わせをすべて試すバックトラッキングアルゴリズムを実装する必要があります。

良いニュースは、限られた2Dスペースに2D長方形を詰め込む必要があるため、早い段階で多くの可能性を整理できるため、それほど悪くないかもしれません。

9
Blindy

必要なのは https://github.com/nothings/stb/blob/master/stb_rect_pack.h

サンプル:

stbrp_context context;

struct stbrp_rect rects[100];

for (int i=0; i< 100; i++)
{
    rects[i].id = i;
    rects[i].w = 100+i;
    rects[i].h = 100+i;
    rects[i].x = 0;
    rects[i].y = 0;
    rects[i].was_packed = 0;
}

int rectsLength = sizeof(rects)/sizeof(rects[0]);

int nodeCount = 4096*2;
struct stbrp_node nodes[nodeCount];


stbrp_init_target(&context, 4096, 4096, nodes, nodeCount);
stbrp_pack_rects(&context, rects, rectsLength);

for (int i=0; i< 100; i++)
{
    printf("rect %i (%hu,%hu) was_packed=%i\n", rects[i].id, rects[i].x, rects[i].y, rects[i].was_packed);
}
2
Orbitus007

一般的な解決策は自明ではありません(数学は完全に****は不可能だと言います)
通常、人々は遺伝的アルゴリズムを使用して可能な組み合わせを試しますが、最初に最大の形状を配置し、次に次の最大の形状に対して別の場所を試すなど、合理的にうまくいくことができます。

1
Martin Beckett

私は次のものを使用しています:

https://codereview.stackexchange.com/questions/179565/incremental-2d-rectangle-bin-packer?newreg=cce6c6101cf349c58423058762fa12b2

ギロチンアルゴリズムを実装し、1つの次元を入力として必要とし、もう1つの次元を最適化しようとします(コードを少し変更するだけで最大値を設定することもできます)。たぶん、2つの値の異なる力を試してみるとうまくいくでしょう。

決して最適ではありませんが、小型で移植性があり(.hのみ)、C++およびSTL以外の依存関係はありません。

0
Adriel Jr