قدم اول در حل مسئله بهینه سازی مقید با استفاده از الگوریتم رقابت استعماری
قدم اول در حل مسئله بهینه سازی مقید با استفاده از الگوریتم رقابت استعماری
مطالعه این پست را به آنهایی که علاقه دارند تا مسائل بهینه سازی مقید خود را با الگوریتم های تکاملی و به طور ویژه با الگوریتم رقابت استعماری حل کنند، توصیه می کنیم. ایده مطرح شده در این پست، کلی بوده و قابل اعمال به همه الگوریتم های تکاملی از جمله الگوریتم های ژنتیک (Genetic Algorithms)، الگوریتم پرندگان یا ازدحام ذرات (Particle Swarm Optimization) و یا کلونی مورچگان (Ant Colony Optimization) می باشد.
برای حل مسائل بهینه سازی مقید، اولین قدم، جلوگیری از تولید جوابهای خارج از محدود پذیرفته شده (Feasible Solutions) است. در کدهای الگوریتم رقابت استعماری برای این منظور چه باید کرد؟
اگر شما از کدهای الگوریتم رقابت استعماری استفاده می کنید، سه تابع وجود دارند که باعث ایجاد تغییرات در موقعیت کشورها (تغییر آنها) می شوند. هر یک از این سه بخش باید مورد بررسی قرار گرفته و تغییراتی در آنها ایجاد شود. نکته قابل ذکر این است که اگر قیود ساده و به صورت بازه ثابت برای هر متغیر باشند، نیازی به انجام این تغییرات نیست و نسخه های مختلف کدهای الگوریتم رقابت استعماری بر روی وب بازه اولیه متغیرها را در اول برنامه گرفته و جوابهای نهایی را نیز مطمئناً در این بازه می دهند. پس منظور از قیود، این نوع قیود ساده نیستند. به مثال زیر توجه کنید.
فرض کنید می خواهیم در یک مسئله بهینه سازی در حوزه مدیریت مالی، از مجموع یک بودجه برداشتی بهینه داشته باشیم. یک تابع هدف نیز داریم. قیدی داریم که مجموع برداشت ها باید یک (100 درصد) باشد. این قید هم باید در تولید جوابهای اولیه و هم در ایجاد تغییرات آرام (تابع جذب) و هم در تغییرات ناگهانی (تابع انقلاب) لحاظ شود.
در ورژن اولیه کدها که معمولاً استفاده می شود، این سه تابع به ترتیب عبارتند از
GenerateNewCountry
AssimilateColonies
RevolveColonies
در هر سه این توابع باید خطوطی از کد را اضافه کنیم که جمع متغیرهای تولیدی را برابر با یک کند. مثلاً تابع GenerateNewCountry در حالت اولیه به صورت زیر است.
كد:
function NewCountry = GenerateNewCountry(NumOfCountries,ProblemParams)
VarMinMatrix = repmat(ProblemParams.VarMin,NumOfCountries,1);
VarMaxMatrix = repmat(ProblemParams.VarMax,NumOfCountries,1);
NewCountry = (VarMaxMatrix - VarMinMatrix) . rand(size(VarMinMatrix)) + VarMinMatrix;
end
با اضافه کردن یک خط می توان مجموع اجزای یک کشور را در مرحله ایجاد برابر با یک کرد. تابع تغییر یافته و خط اضافه شده را در زیر می بینید.
كد:
function NewCountry = GenerateNewCountry(NumOfCountries,ProblemParams)
VarMinMatrix = repmat(ProblemParams.VarMin,NumOfCountries,1);
VarMaxMatrix = repmat(ProblemParams.VarMax,NumOfCountries,1);
NewCountry = (VarMaxMatrix - VarMinMatrix) . rand(size(VarMinMatrix)) + VarMinMatrix;
%% Normalizing the sum of population to 1
NewCountry = NewCountry . repmat(sum(NewCountry,2),1,ProblemParams.NPar);
end
لازم به ذکر است که نرمالیزه کردن یک کشور (یک کردن مجموع اجزای آن) ممکن است در موارد معدودی باعث خروج از قیود بازه اولیه نیز بشود. بنابراین توصیه می شود اگر قید جدیدی را اضافه می کنید و تغییرات در موقعیت کشور (Position)، می دهید، درست بودن قیدهای قبلی را دوباره چک کنید. این کار باید در یک حلقه آنقدر تکرار شود که هنگام خروج از تابع همه قیود صادق باشند. ما در مورد بالا این کار را انجام ندادیم (و البته در حل مسئله خاصی که مدنظر بود، ظاهراً مشکلی ایجاد نمی شد)، اما شما حتماً این موضوع را در نظر بگیرید.
تغییرات مشابهی در توابع بعدی نیز باید ایجاد شود. دو تابع بعدی را به ترتیب در زیر آورده ایم.
function TheEmpire = AssimilateColonies(TheEmpire,AlgorithmParams,ProblemParams)
NumOfColonies = size(TheEmpire.ColoniesPosition,1);
Vector = repmat(TheEmpire.ImperialistPosition,NumOfColonies,1)-TheEmpire.ColoniesPosition;
TheEmpire.ColoniesPosition = TheEmpire.ColoniesPosition + 2 AlgorithmParams.AssimilationCoefficient rand(size(Vector)) . Vector;
%% Modifying Population according to Conditions
MinVarMatrix = repmat(ProblemParams.VarMin,NumOfColonies,1);
MaxVarMatrix = repmat(ProblemParams.VarMax,NumOfColonies,1);
TheEmpire.ColoniesPosition=max(TheEmpire.ColoniesPosition,MinVarMatrix);
TheEmpire.ColoniesPosition=min(TheEmpire.ColoniesPosition,MaxVarMatrix);
TheEmpire.ColoniesPosition = TheEmpire.ColoniesPosition . repmat(sum(TheEmpire.ColoniesPosition,2),1,ProblemParams.NPar);
end
تابع RevolveColonies در حالت اولیه
كد:
function TheEmpire = RevolveColonies(TheEmpire,AlgorithmParams,ProblemParams)
NumOfRevolvingColonies = round(AlgorithmParams.RevolutionRate numel(TheEmpire.ColoniesCost));
RevolvedPosition = GenerateNewCountry(NumOfRevolvingColonies , ProblemParams);
R = randperm(numel(TheEmpire.ColoniesCost));
R = R(1NumOfRevolvingColonies);
TheEmpire.ColoniesPosition(R,) = RevolvedPosition;
MinVarMatrix = repmat(ProblemParams.VarMin,NumOfColonies,1);
MaxVarMatrix = repmat(ProblemParams.VarMax,NumOfColonies,1);
TheEmpire.ColoniesPosition=max(TheEmpire.ColoniesPosition,MinVarMatrix);
TheEmpire.ColoniesPosition=min(TheEmpire.ColoniesPosition,MaxVarMatrix);
end
تابع RevolveColonies در حالت تغییر یافته
كد:
function TheEmpire = RevolveColonies(TheEmpire,AlgorithmParams,ProblemParams)
NumOfRevolvingColonies = round(AlgorithmParams.RevolutionRate numel(TheEmpire.ColoniesCost));
RevolvedPosition = GenerateNewCountry(NumOfRevolvingColonies , ProblemParams);
R = randperm(numel(TheEmpire.ColoniesCost));
R = R(1NumOfRevolvingColonies);
TheEmpire.ColoniesPosition(R,) = RevolvedPosition;
%% Modifying Population according to Conditions
NumOfColonies = size(TheEmpire.ColoniesPosition,1);
MinVarMatrix = repmat(ProblemParams.VarMin,NumOfColonies,1);
MaxVarMatrix = repmat(ProblemParams.VarMax,NumOfColonies,1);
TheEmpire.ColoniesPosition=max(TheEmpire.ColoniesPosition,MinVarMatrix);
TheEmpire.ColoniesPosition=min(TheEmpire.ColoniesPosition,MaxVarMatrix);
TheEmpire.ColoniesPosition = TheEmpire.ColoniesPosition . repmat(sum(TheEmpire.ColoniesPosition,2),1,ProblemParams.NPar);
end
با ایجاد این تغییرات تضمین می شود که جواب بهینه نهایی مسئله مجموع یک خواهد داشت.
در زیر نمودار همگرایی مسئله را می بینیم.
توجه شود که ایجاد تغییرات در توابع تولید کننده و اصلاح کننده جمعیت کشورها اولین و موثرترین قدم در راه حل مسائل بهینه سازی مقید می باشد، ولی تنها راه نیست. در بسیاری موارد قیود ما آنقدر پیچیده هستند که نمی توان آنها را در مرحله تولید کشور ها لحاظ کرد.