算問題:將N個大小不等的矩形不重疊地拼在一個指定寬度不定長度的大矩形里,求使占用大矩形區域最小的拼法。


將N個大小不等的矩形(長或寬不大於大矩形的指定寬度)不重疊地拼在一個指定寬度不定長度的大矩形里,求使占用大矩形區域最小的拼法。 
我的思路是先把N的N!種可全排列獲取出來然后再去拼接算出總面積用快速排序法獲取最小面積.但問題是N!的可能性太多了.當N>10時等待的時間就沒辦法忍受了. 
大家都來出個主意拼個快速的算法.

107 个解决方案

#1


矩形能夠旋轉么?感覺有點像俄羅斯方塊!(其實我也奇怪自己為什么要問這個問題?不是憑空增加難度么!)

LZ給出的方法其實也有問題,即使N!都列出來了,還有個空間擺放的問題,那樣組合起來可就沒邊了!

如果這N種矩形沒有一個給定好的規律,這題應當屬於NP級的,應該很難求出最優解,只能用一些近似算法吧!

另外這個占用面積的定義,也應該明確一下,否則歧義也挺多的!

#2


矩形能夠旋轉么?這個問題問的好,呵呵,為達到節省空間的目的矩形是可旋轉的.從原理上來講確實像是俄羅斯方塊中的長條組合.這里的N個矩形我們可以理解為俄羅斯方塊中(2,3,4)個點的的長條不定量.
占用面積,還是用俄羅斯方塊來理解,即已經疊上去的長條方塊占最高的點為占用面積的長,區域寬為占用面積的寬.

#3


這是寶鋼冷軋鋼還是熱軋鋼的實際生產問題

軋鋼機出的寬度是固定的,長度可以不定,反正就是鋼水冷卻之類的,理論上要多長有多長

而生產任務是不同的,需求的鋼板大小數量都是可變的

那么切割的時候就會有邊角料

所以希望有個優化算法可以使邊角料最少

寶鋼本身采用的是日本的優化程序

通過分析發現只是個專家數據庫

日本人把歷史上人為分析結果存儲進去,通過積累,然后比對找到次最優結果

寶鋼想用自己的算法,可惜是np-hard問題,很難超越日本人的做法

#4


嘿,原來這個問題還引出這么有趣的事,看來是挺難解決的一個問題.我原意只是想用來分析裁紙.N<=10的情況下用我提到的思路去做還是比較快能找到結果的.最多也就一兩分鍾的事,但當N>10時,就不得了了~~
希望大家繼續發表意思啊~~

#5


感覺需要用一些可以處理指數級的近似算法,求近似解,最優解恐怕夠嗆了!
LZ可以看看A*算法!

#6


是夠嗆的.摸不着頭腦啊~~

#7


引用 3 樓 laozi 的回復:
寶鋼想用自己的算法,可惜是np-hard問題,很難超越日本人的做法


非常有趣!!求教:如何證明此問題是NP-Hard,而不是NPC??

#8


看看。

#9


hoho,奶油狗的問題真夠辣手的

證明npc的通常做法看能否轉換為已知npc問題

http://en.wikipedia.org/wiki/List_of_NP-complete_problems

這里是維基百科里的npc問題列表

另外補充原題的是,子矩陣的長短邊是要和大矩陣長短邊平行的(這可以簡化問題,同時也是現實需求,為了便於切割)

這道題其實已經數學模型化了,我還看到過一個類似問題的擴展

有若干任務需要完成,每個任務完成的時間和同時占用的生產線數量不同,可以認為有m個生產線,求生產時間最短的算法

這里時間軸就是大矩陣長邊,生產線數量是大矩陣短邊

但是回過頭再看,這種任務優化適用范圍更廣,現在只是二維的

三維的時候呢?

多維的時候呢?

而解決了多維問題,我估計sap會請你做全球副總裁了

很希望這個帖子里大家都討論出些內容來

#10


二維矩形排樣(2D-SPP)算法,人工智能演化計算機,可以采用Bottom-Left原則,用模擬退火算法(SA)或者粒子流(PSO)算法求近似最優解。

#11


做了幾年程序了,總沒什么成就感。
看了這個問題之后,發覺有方向了。謝謝樓主

每一個程序員,尤其中國程序員,都該好好關注下這種問題,人多力量大。說不定哪天蘋果掉到我頭上,我就解決它了。

#12


朋友稍安勿躁,我查了相關資歷,這個是npc問題,具體能轉換成那個已知npc問題還沒搞清楚

但是如果你能解決這個npc問題,那么整個一類npc問題就不存在了

解決次優解問題吧

或是多維問題的二維轉換,這個是我們能夠奢望自己可以做的事

#13


我看了一款大型噴繪機操作軟件.其中有個自動排列功能.在導入需要打印的文檔時,很快速的自動翻轉排列好,但它也不是最優解.是按導入文件的順序做一個旋轉適應排列

#14


拋磚引玉講一下我分析日本專家系統的工作原理

首先假設專家數據庫為空,然后輸入人工分析出來的常見生產任務的最優解

比如a規格鋼板n1個,b規格鋼板n2個,c規格鋼板n3個

需要的大矩形長度以及切割位置

當任務有若干個后,專家系統里就有許多最優解集合

突然來了個特別的任務,不同於以前任何任務組合

專家系統進行內部調整,把這次任務群分解成已知最優解的組合

這個算法是很好寫的,類似解方程組,通過矩陣運算就能處理了

但是寶鋼只有使用權,就是輸入任務得出輸出結果,沒有修改權擴充這個專家系統,使專家系統更接近最優解

因此需要每年再向日本買新版本的專家數據庫(或是lisence的問題)

該項目是我們學校博導去談的,他是數學系出身,專攻調度優化

很遺憾地告訴大家,他也做不出一個比日本的專家系統更好的系統來

#15


雖然做不出更好的,做個一樣的也行呀,至少不用每年向日本購買新的專家數據庫!

引用 14 樓 laozi 的回復:
拋磚引玉講一下我分析日本專家系統的工作原理 

首先假設專家數據庫為空,然后輸入人工分析出來的常見生產任務的最優解 

比如a規格鋼板n1個,b規格鋼板n2個,c規格鋼板n3個 

需要的大矩形長度以及切割位置 

當任務有若干個后,專家系統里就有許多最優解集合 

突然來了個特別的任務,不同於以前任何任務組合 

專家系統進行內部調整,把這次任務群分解成已知最優解的組合 

這個算法是很好寫的,類似解方程組,…

#16


呵呵,寶鋼是大型國企,走學校的資金是研究性的

也就是結果不一定要出來,但是需要論文一大堆,這樣就能批到資金了

如果要做個一樣的,寶鋼有自己的軟件公司的

#17


哦,原來如此!

別看寶鋼屬於基礎行業,離IT相對較遠,但仍然注重基礎研究工作,以及配套的軟件工作。國內許多大型國企及上市公司,
可能都沒有1項屬於自己的專利技術.軟件研發方面從不建設自己的團隊,走得全都是外包或直接從國外購買。
企業規模較小的時候,我們也提倡外包,不過買賣都做到那么大了,自己的研發方面還幾乎為0,就有些說不過去了。

引用 16 樓 laozi 的回復:
呵呵,寶鋼是大型國企,走學校的資金是研究性的 

也就是結果不一定要出來,但是需要論文一大堆,這樣就能批到資金了 

如果要做個一樣的,寶鋼有自己的軟件公司的

#18


sas第一次正式的進入中國企業就是從寶鋼開始的

我那時讀大學,96年的時候吧,現在sas已經如日中天了

#19


引用 18 樓 laozi 的回復:
sas第一次正式的進入中國企業就是從寶鋼開始的 

我那時讀大學,96年的時候吧,現在sas已經如日中天了

老大哥啊,做程序十多年了啊~贊一個.以后多幫助小弟哦~~

#20


學習啊!

#21


幫頂一下

#22


怎么幫,沒法幫

都說程序員是吃青春飯

每年都有新的概念,如果你追這些新概念,肯定比不上年青人(老人都有固有思維的,多少會排斥新思維的)

做管理?不喜歡

寫核心代碼?不行,都是拿別人的,有多少人可以提出自己的有效算法,發表的論文都是在已有基礎上的小改進,做報表對比的時候心里那個慌啊,盡挑好的數據,差的數據都選擇性無視了

現在唯一的成就就是集成,什么是集成,拿別人的東西完成具體的項目,積木(開源代碼)就在那里,東拼一點兒,西拼一點兒,一個項目就搞定了

別人在那里點頭,完成速度挺快的啊!功能挺好的啊!

但是我的心是慌的

還是學數學的好,到這里看看mathe他們的解題思路是一種享受

#23


LZ的描述不夠詳細。許多東西都不清楚。但是可以肯定的是像類似的實時性NP問題不建議采用退火等迭代方法,也盡量減小計算規模。N階的計算規模上升速度僅次於指數上升。像這種可以算的上是對時間開銷重視遠大於空間的情況采用可分布的算法最好。

我認為,不一定一定需要每天或者是貨單來了,我再來算我如何來排如何放置這些待加工的板塊,試想,長度是可以想要多少就有多少的,而現在客戶的需要的組合我不管如何都找不到一種最優秀的排法,或者說是無法在較短的時間內找到優秀的解決方案,而與此組合相近的另一種已知組合可以滿足客戶的需要,雖然多大了幾快板子出來,但是余料很理想,且剩余的板子也很符合由前斷時間以來的數據所預測的下次或者說是短期內的客戶需求的各種板材型號的比例,或者較為符合。再不理想的情況,我多大了幾塊板子出來。不符合客戶需要的比例,但是也比余料重新回爐要強的多吧。

第一個過程是找基准的基准。最理想的情況(余量)最少的那幾種或是十幾,幾十種組合你是可以確定的。因為每天的產量固定。即使浮動也不會有巨大的震盪,那么你就吧每天的產量用軋鋼機寬,把面積或者說長度先固定下來。這就是理想情況。然后就開始以你公司的產品型號進行組合,先取較大步長。算出理想狀態下的線性規划的理想組合。當然不會是整數,然后以這個理想值為基礎,做局部搜索,找出所有變量都為整數的此種組合下的最優解。
第二步縮進步長,找出這些種組合中與往年數據比較接近的組合,然后在此范圍內進行精確計算。
第三步就是存儲已知的非常優秀的板塊組合數據。
第四步,由近期的產量結構做短期內的數學期望和擬合。

前期的工作基本完成。然后是客戶貨單來了以后,首先就是看看最接近哪一個數據了,然后看差值是否滿足上四,如果滿足,執行。不滿足,繼續看余次優方案是否滿足上四。如此循環,當然需要加上權重條件。比如。考慮的到底是實時性高些?畢竟要考慮存貯的維護開銷。還是保證產品的結構層次更合理?等等等等。




其實需要考慮的問題是在是太多了。以上就是我的一些初步的看法。

#24


我做過這方面的論文,對這個優化有很好的結果。
jeffrey dot nupt at gmail dot com

#25


去寶鋼推薦一下啦,不過如果是寶鋼上面的蛀蟲拿回扣,非要從日本更新專家數據庫,那就沒有任何意義了

#26


貪心+背包
我的想法。。。
呵呵

#27


矩形可以旋轉任意角度。

#28


也無需任意角度,就90度旋轉就OK了.

#29


的確是個非常困難的優化問題,長見識了

#30


NP難問題  多找寫論文看看

#31


呵呵 這道題曾經是去年微軟校園招聘的一道筆試題,曾經試過用貪心,時間太緊寫的很亂,也沒有好的想法。
不過感覺NP可以解的

#32


關注,希望lz給出一些實際例子,
畢竟客戶要求的鋼板還是有一定規格的,而不是任意值,至少是個有限集,
從這個角度看,認真分析統計客戶的需求,也許可以簡化問題,從而解決問題

#33


最近在做了一個這樣的算法,問題和lz的一樣,就是將大小不等的矩形圖片排列到一個大矩形里,大矩形的寬度必須是2的冪,主要是給美工用的,因為要做游戲引擎,想把每一個關卡的圖片一次性加載到內存里,美工要根據這個做一張大圖片,要求也是矩形盡可能的小。(平台很變態所以不能一一加載圖片)
曾試過用遺傳算法做,可是每次計算適應度相當於排一次圖片,很費時間。最后也是采用貪心的思想,先將所有的矩形按邊長從大到小排序,然后按順序排放,擺放過程中不停的更新鋒線,所謂的鋒線就是已經被占用的地方和沒有被占用的地方的分界線,然后找鋒線中最靠上的線段開始擺放(假設是從上到下擺放),如果當前矩形不符合條件就試下一個,如果都不符合條件就將這條線段下移到它附近最靠上的位置 繼續從第一個沒有被擺放的矩形開始擺......
最后的效果是上面的擺放很好,幾乎沒有縫隙,而下面的就不行了,因為供選擇的圖片太少,最后不得已做了一個可以人為移動小矩形的工具。先按上面的算法計算一次,然后再人工的調整一下。這樣得到的結果不會是最優的,但是還可以接受。
希望能給樓主一點啟發,同時也征集比較好的算法,畢竟讓人干預有點太傻了。

#34


學習中哦 !@#!@






















 ---------------------------------------------------
公主小游戲
喜羊羊與灰太狼小游戲

#35


Intel07年有個多核算法大賽,有這個題,

#36


計算幾何說這是經典了啊

#37


這個題比前幾天那個三維拼圖的題簡單得多,為什么沒人做呢?

#38


引用 2 樓 beargo 的回復:
矩形能夠旋轉么?這個問題問的好,呵呵,為達到節省空間的目的矩形是可旋轉的.從原理上來講確實像是俄羅斯方塊中的長條組合.這里的N個矩形我們可以理解為俄羅斯方塊中(2,3,4)個點的的長條不定量.
占用面積,還是用俄羅斯方塊來理解,即已經疊上去的長條方塊占最高的點為占用面積的長,區域寬為占用面積的寬.

例如長度為2,3,4的長條分別有5,3,4個,要放入一個寬為7的矩形中,求如何放置使高度最小。
用動態規划寫了一個,還沒有做算法優化:
matlab程序:
clear;
clc;
myinf=100000000000;
space=zeros(8,7);
leni=size(space,1);
lenj=size(space,2);
barw=... 
    {[1,1]
    [1,1,1]
    [1,1,1,1]};
barh=...
    {[1,1]'
    [1,1,1]'
    [1,1,1,1]'};
nbar=[5,3,4];%各條數量
ntotl=sum(nbar);%所有長條總數
nbartype=size(nbar,2);%長條種數
%初狀態加入狀態列表
statlist(1)=struct('space',zeros(leni,lenj),'restnbar',nbar,'open',true,...
    'h',0,...%填充高度
    'usednbar',0,...%已用條數
    'nsq',0 ...%已填格數
    );
while 1
    nls=size(statlist,2);
    disp(nls);
    %找open==true且h/nsq最小的狀態
    mn=myinf;
    mni=-1;
    for i=1:nls
        if statlist(i).open==true
            valu=statlist(i).h/(statlist(i).nsq+1);
            if valu<mn
                mn=valu;
                mni=i;
            end
        end
    end
    statlist(mni).open=false;
    stat=statlist(mni);
    %終止
    if stat.usednbar==ntotl
        break;
    end
    %推進stat
%     disp('stat:');
%     stat.space
    for i=1:nbartype%對每種長條i
        if stat.restnbar(i)~=0%如果還有
            for k=1:2
                if k==1
                    %將barw{i}放入
                    h=size(barw{i},1);
                    w=size(barw{i},2);
                else
                    %將barh{i}放入
                    h=size(barh{i},1);
                    w=size(barh{i},2);
                end
                for j=1:lenj-w+1
                    %將bar{i}(左上角坐標為j)放入轉移到linstat
                    linstat=stat;
                    %求bar{i}投影區間上已填充的高度
                    gotpeak=false;
                    for u=1:leni
                        for v=j:j+w-1
                            if linstat.space(u,v)~=0
                                gotpeak=true;
                                break;
                            end
                        end
                        if gotpeak==true;break;end;
                    end
%                     disp(['u:',num2str(u)]);
%                     disp(['gotpeak:',num2str(gotpeak)]);
                    if gotpeak==false
                        u=u+1;
                    end
                    %得到u
                    tgi=u-h;
                    tgj=j;
                  %  disp(['(tgi,tgj)',num2str(tgi),',',num2str(tgj)]);
                    %貼圖坐標為linstat.space(tgi,tgj)
                    %貼圖
                    color=floor(rand(1,1)*90+5);%隨機生成顏色值
                    for u=tgi:tgi+h-1
                        for v=tgj:tgj+w-1
                            linstat.space(u,v)=color;
                        end
                    end      
                    linstat.restnbar(i)=linstat.restnbar(i)-1;
                    linstat.open=true;
                    linstat.h=leni-tgi+1;
                    linstat.usednbar=linstat.usednbar+1;
                    linstat.nsq=linstat.nsq+w*h;
%                         disp(linstat.space);
%                         disp(['restnbar:',num2str(linstat.restnbar)]);
%                         disp(['usednbar:',num2str(linstat.usednbar)]);
%                         disp(['nsq:',num2str(linstat.nsq)]);
%                         disp(sum(sum(linstat.space~=0)));     
                     %   pause;
                    %將linstat加入statlist
                    %看linstat是否已存在於statlist
                    exist=false;
                    for u=1:nls
                        if all(all(statlist(u).space==linstat.space))...
                                &&all(statlist(u).restnbar==linstat.restnbar)
                            exist=true;
                            break;
                        end
                    end
                    if exist==true
                        statlist(u).open=true;
                    else
                        statlist(end+1)=linstat;
                    end
                end    
            end
          %  disp('hi');
        end
    end
end
%文字結果
disp(stat.space);
%圖形結果
rs=stat.space./100;
image(rs,'CDataMapping','scaled');
運行結果:
     0     0     0     0     0     0     0
     0     0     0     0     0     0     0
     0     0     0     0     0     0     0
     8     8     8    15    15    44    44
    57    57    57    57    77    77    82
    77    77    77    77    22    22    82
    74    74    74    74    34    34    34
    47    47    47    47    28    28    28

#39


再來頂~·

#40


mark

#41


沒有什么好辦法,好像只能局部優化.
1.先用貪心法求得一個最大高度.
2.循環測試全排序,可以排除幾種情況:大於等於最大高度的時候,停止子排序的測試;因為最優解上下左右都可倒置,可以過濾掉已遍歷的倒置序列,減少3/4的數據量.

#42


再來頂~·

#43


頂一下吧,我也正需要這方面的,想用C#做一下,沒研究出來.

#44


http://www.blackpawn.com/texts/lightmaps/

#45



有那么難的嘛,我覺得很簡單的啊。
    依據:正方形面積最小。

    做法: 將N個大小不等的矩形拼成一個正方形(盡量正規是的正規的正方形),如果這個
正方形的邊長小於 給定大矩形的寬度時,就用這種方法了。如果邊長大於給定的矩形的寬度時,
就依給定矩形的寬度為邊長,拼成一個等邊長的矩形了。

    當然,如果N個矩形拼湊時,盡量的使浪費的空間最小。
想法是這樣,不過做的話嘛,就不知道了。

#46


看來真的該研究算法了。。。。。真失敗啊

#47


邏輯題。

#48


路過~友誼一頂

#49


頂個~~~~

#50


路過也友誼頂個~

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com