算法設計與分析 項目
工廠開設
1 題目描述
2 題目分析及解題思路
2.1 開發環境
- MATLAB R2016a
由於本題目會使用大量的向量、矩陣操作,爲了方便及高效運算,使用MATLAB開發。
2.2 解題思路
2.2.1 解題方法
針對本題目,準備使用三種方法來解,分別是
Local Search 顧客選擇工廠
、Local Search 工廠選擇顧客
、模擬退火
。
2.2.2 編碼方式
在使用三種方法解得過程中,編碼方式是十分重要的,結合此題目,使用字符串來表示解,形式爲:角標對應顧客序號,數組值對應所在工廠序號。
例如解:1 2 1 3....
代表:1號顧客分配去1號廠;2號顧客分配去2號廠;3號顧客分配去1號廠;4號顧客分配去3號廠…
2.3 Local Search 顧客選擇工廠
查看幾個算例後發現,其實題目中的例子開設工廠和顧客參與工廠的開銷差別不是很大,因此,採取了優先顧客的原則,對於每一位顧客,將他們安排到開銷最小的工廠中去,如果該工廠已經滿員,那麼就安排他去開銷第二小的工廠,以此類推。
2.4 Local Search 工廠選擇顧客
同樣的,如果能夠省下工廠的開銷,那麼也會是一筆不小的數額,因此,採取有限工廠的原則,對於每一個工廠,選擇開銷最小的顧客直到工廠滿員。首先開啓第一間工廠,依次選擇開銷最小的顧客直到滿員;之後開啓第二間工廠,直到所有的顧客都被安排的明明白白。
2.5 模擬退火算法
在每一輪隨機生成新的解,如果生成的新的解比現有的解更好,則接受;否則以一定的概率接受。此概率隨着迭代的進行不斷減少。
當迭代到一定次數,或連續多次沒有接收更加優質的解,視爲尋找到最優解。
3 算法實現
3.1 Local Search 顧客選擇工廠
FOR i = 1: CustomeNum
WHILE TRUE
FIND_MIN_COST_FACTORY
IF FACTORY_CAPILITY > CUSTOMER_CAPILITYFACTORY -= CUSTOMER_CAPILITY
BREAKENDIF
ENDWHILE
ENDFOR
3.2 Local Search 工廠選擇顧客
ARRANGE_NUM = 0;
OPEN_FACTORY = 1;
WHILE ARRANGE != CUSTOMER_NUMFIND_MIN_COST_CUSTOMER
IF FACTORY_CAPILITY > CUSTOMER_CAPILITYFACTORY -= CUSTOMER_CAPILITY
RESULT(CUSTOMER) = OPEN_FACTORY
CONTINUEENDIF
OPEN_FACTORY++ENDWHILE
3.3 模擬退火算法
3.3.1 參數設計
由於算例較多,因此設計一套使用所有的參數體系。
參數 | 設計 |
---|---|
T(初溫) | 隨機生成(顧客數/10)個解,取花費的均值的平方 |
MIN_T(最低溫) | T的倒數 |
FORBID_MAX (最大無用解個數) | 顧客數 * 工廠數 ^ 2 |
dropSpeed(降溫速率) | 0.99 |
INSIDE_NUM(內層循環數) | 顧客數 * 工廠數 |
T(初溫) | 隨機生成(顧客數/10)個解,取花費的均值的平方 |
3.3.2 算法
INITIAL(SOLUTION)
WHILE FORBID_NUM < FORBID_MAX && T > MIN_TFOR i = 1: INSIDE_NUM
NEWSOLUTE = CREATE_NEW_SOLUTION(SOLUTION)
IF ESTIMATE(NEWSOLUTION) < ESTIMATE(SOLUTION)SOLUTION = NEWSOLUTION
FORBID_NUM = 0ELSE
RATE = RANDOM(0, 1)
IF RATE < EXP(-DELT(E )/ T)SOLUTION = NEWSOLUTION
ELSE
FORBID_NUM++
ENDIF
ENDIF
ENDFOR
T *= DROP_SPEEDENDWHILE
3.3.3 產生新解過程
採取3種新解的方法:
- 1.隨機改變一個顧客所在工廠。
- 2.隨機改變N(N < CUSTOMER_NUM)個顧客所在工廠。
- 3.隨機對任意一段進行排序。
3.3.4 修正解
只修正錯誤顧客。
從第一位顧客開始記錄,當工廠容量不足,將超額的顧客序號記錄。
對於超額的顧客,依次安排在剩餘未超額的花費最小的工廠中。
3.4 Local Search作爲初始的模擬退火算法
與模擬算法類似,但使用了Local Search 的解作爲初始解。
4 關鍵代碼
4.1 Local Search 顧客選擇工廠
% arrange every customer
for i = 1: cusNum
while 1
% find the most adaptor function of this customer
x = find(tempCus(i, :) == min(tempCus(i, :)));
x = x(1, 1);
% function has capiility, arrange; else, put this value to INF
if tempCap(x, 1) - cusCap(i, 1) > 0
tempCap(x, 1) = tempCap(x, 1) - cusCap(i, 1);
result(i, 1) = x;
break
else
tempCus(i, x) = 10000;
end
end
end
4.2 Local Search 工廠選擇顧客
while 1
% if all of customer has been arranged
if currentCus == cusNum
break;
end
% find the most adaptor customer
x = find(tempCus(:, currentFunc) == min(tempCus(:, currentFunc)));
x = x(1, 1);
% if function has capility, arrange customer
if tempCap(currentFunc, 1) - cusCap(x, 1) > 0
tempCap(currentFunc, 1) = tempCap(currentFunc, 1) - cusCap(x, 1);
result(x, 1) = currentFunc;
tempCus(x, :) = 10000;
currentCus = currentCus + 1;
continue;
end
% if current function conn't aaccept customer, open a new function
tempCus(:, currentFunc) = 10000;
currentFunc = currentFunc + 1
end
4.3 模擬退火算法
4.3.1 主要框架
%外循環
%當溫度足夠高並且產生的連續廢棄解不夠多就繼續迭代
while T > minT && wrongRes < generateNum
%內循環
for i = 1 : cusNum * funcNum
%生成新解
newSolu = newSolution( solution, cap, cusCap, cusCost );
deltaE = estimate( solution, openCost, cusCost ) - estimate( newSolu, openCost, cusCost );
%新解很好,可以使用,否則一定概率拋棄
if deltaE > 0
solution = newSolu;
wrongRes = 0;
else
%如果低於接受概率,需要拋棄,並增加失敗解的數量;否則接受
randomRate = unifrnd (0,1,1);
acceptRate = exp(-deltaE / T);
if randomRate > acceptRate
solution = newSolu;
wrongRes = 0;
else
wrongRes = wrongRes + 1;
end
end
end
%降溫並記錄迭代次數
T = T * dropSpeed;
iterateNum = iterateNum + 1;
% fprintf('iterateNum: %d, solution: %f\n', iterateNum, estimate( solution, openCost, cusCost ));
end
4.3.2 產生新解
4.3.3 修改解
% find all customer who cann't be arrange
for i = 1: cusNum
tempCap(source(i, 1), 1) = tempCap(source(i, 1), 1) - cusCap(i, 1);
if tempCap(source(i, 1), 1) < 0
wrong = [wrong; i];
end
end
tempCus = cusCost;
% function cann;t accept customer whose capility is minus
for i = 1: funcNum
if tempCap(i, 1) < 0
tempCus(:, i) = 10000;
end
end
% adjust wrong customer location
tempCus = cusCost;
[wrongNum, ~] = size(wrong);
for i = 1: wrongNum
currentCus = wrong(i, 1);
while 1
% find the most adaptor function of this customer
x = find(tempCus(currentCus, :) == min(tempCus(currentCus, :)));
x = x(1, 1);
% function has capiility, arrange; else, put this value to INF
if tempCap(x, 1) - cusCap(currentCus, 1) > 0
tempCap(x, 1) = tempCap(x, 1) - cusCap(currentCus, 1);
result(currentCus, 1) = x;
break
else
tempCus(currentCus, x) = 10000;
end
end
end
4.4 其他
4.4.1 數據讀取與解析
% read file
data = textread(fileName);
% initial function number and customer number
funcNum = data(1,1);
cusNum = data(1,2);
% initial function capitial and open cost
cap = zeros(funcNum, 1);
openCost = zeros(funcNum, 1);
for i = 1: funcNum
cap(i, 1) = data(i + 1, 1);
openCost(i, 1) = data(i + 1, 2);
end
% initial customer capility and cost
rpc = cusNum / 10;
rpf = funcNum / 10;
% get customer capility
cusCap = data(funcNum + 2: funcNum + rpc + 1, :);
% transform and reshape
cusCap = reshape(cusCap', cusNum, 1);
% get customer cost
cusCost = zeros(cusNum, funcNum);
% get data
tempCost = data(funcNum + cusNum / 10 + 2 : funcNum + (cusNum + cusNum * funcNum) / 10 + 1, :);
%reshape
for i = 1: cusNum
cusCost(i, :) = reshape(tempCost((i - 1) * rpf + 1: i * rpf, :)', funcNum, 1);
end
4.4.2 價值評估
% get cost of customer
for i = 1: m
cost = cost + cusCost(i, result(i, 1));
end
func = unique(result);
% get open function number
[openFuncNum, ~] = size(func);
% get cost of function
for i = 1: openFuncNum
cost = cost + openCost(func(i, 1), 1);
end
5 錯誤數據修改
給出的算例中,p27、p47有嚴重錯誤。修改如下:
- 1.將p27數據後的無用數據全部刪除
- 2.將p47數據的缺失數據用下一行相應位置補齊,如圖:
6 結果分析
6.1 Local Search比較
首先比較兩個
Local search
的結果,很明顯的發現,第一種的結果要好於第二種;且兩種算法的時間都很短。
6.2 LocalSearch與模擬退火比較
很明顯的看出,模擬退火需要大量的時間,但是得到的解普遍要優於Local Search。
6.3 使用Local Search作爲初始解的模擬退火
很明顯的發現,用時短了很多,但是得到的解質量卻沒有隨機生成的解好。