当下时代,计算机无处不在,我们的生活早已与电脑手机等电子设备融为一体。“算法”,我相信这个词你已经听过了无数遍,但你可能并不了解他的真实面目。今天,我将要开启一个全新的栏目,讲述数学模型与算法。
初识算法
算法到底是是么?我相信这个问题困扰了不少人。
概括的说,算法就是解决问题的方法,有着具体的过程与步骤,并且这些步骤的次数是有限的,不会无限重复下去。算法具有“死板”的特性,这与计算机程序相类似,所以算法能够与计算机适配的相当好。
每一种算法,其所能解决的问题不不尽相同的。例如快速排序算法针对于排序问题,K均值算法能够将已有数据根据特征分类,今天将要谈到的模拟退火算法,则是用于求解问题的最优解。
算法解释
模拟退火算法 (Simulated Annealing,SA) 最早的思想是由 N. Metropolis 等人于1953年提出。1983年, S. Kirkpatrick 等成功地将退火思想引入到组合优化领域。它是基于 Monte-Carlo 迭代求解策略的一种随机寻优算法,其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。模拟退火算法从某一较高初温出发,伴随温度参数的不断下降,结合概率突跳特性在解空间中随机寻找目标函数的全局最优解,即在局部最优解能概率性地跳出并最终趋于全局最优。
这么说起来可能略显混乱,那就让我们回到其出发点:固体退火上来理解。
处在低温状态时,固体中分子具有的内能很低,在原本的位置上做小范围的振动。若是将固体加热到一定温度,分子内能将会增加,热运动加剧,分子排列的无序度增加。此时再将温度缓缓降低,在每个温度都达到平衡态(即准静态过程),分子具有的能量逐渐降低,最终回归到有序排列的状态,分子内能也跟着降到最低。
若是将其抽象成具体问题上来说,可以将固体温度视为算法进行的次数,分子内能看做问题的解。对于一个函数最小值问题来说,可以先在定义域内随机选取一个点作为初始值x0,可以计算出第一个解y0并将其视为最小值。随后对初始值进行扰动(可以是x1=x0+dx)此时的将会对应一个函数值y1,将y1与y0进行比较,若是小于,则将其视为新的最小值,反之维持原状。如此循环往复多次,即可寻找出函数的最小值。
但上述方法有一个致命的缺点:容易陷入局部最优解(或者说极值) 。
对于一个具体的函数
函数图像
若是选取x=0为初始值(红点),按照上述步骤,最终迭代结果将会是在x=-0.9处取得最小值(蓝点)。但很显然,最小值是在x=-4处(黑点)取得的。
这问题看似挺无解,但是这正是模拟退火最奇妙的地方。
再次回到固体降温。热力学中Boltzman 概率分布告诉我们:同一个温度,分子停留在能量小状态的概率大于停留在能量大状态的概率。温度越高,不同能量状态对应的概率相差越小,温度足够高时,各状态对应概率基本相同。随着温度的下降,能量最低状态对应概率越来越大,温度趋于0时,其状态趋于1。讲人话就是当处在某一温度时,大多数分子具有的能量较低且处于稳定状态。但是,还有少数分子“特立独行”,具有较大的内能,热运动较与其他分子更为剧烈。当温度逐渐降低,处于低内能状态的分子越来越多,最终绝大多数的分子内能降到最低并稳定下来。
模拟退火算法利用了Boltzman 概率分布,当遇到不好的结果时,算法不会立刻否决掉,而是会以一定概率接受这个解(状态转移)。这一过程被称为Metropolis准则:
在循环初期,转移概率较大,会接受大部分的差解,给与更多的机会探索以跳出局部最优。随着循环次数增加,转移概率渐渐降低,差解越来越难被接受。最终,循环(降温)结束,解趋于稳定(内能降到最低)。
算法步骤
讲了这么多,我们该讲一下模拟退火具体该怎么施行了。以下是算法具体步骤:
1、设定初始温度,在解空间中随机选取一个作为初始解,并将其视为当前最优解
2、对初始解进行扰动,得到一个新的解
将新解与旧解进行比较,并依据Metropolis准则判断是否接受新解作为当前最优解
4、重复步骤2与步骤3,直至降温结束
5、输出结果
若是文字看不太明白,可以看看流程图:
其中有几个需要注意的点:
1、初始点的选取对算法结果有一定的影响,最好是多次运行对结果进行综合判断。
2、在算法运行初期,温度下降快,避免接受过多的差结果。当运行时间增加,温度下降减缓,以便于更快稳定结果。
3、当迭代次数增加到一定次数时,结果可能已经达到稳定,但是距离算法结束还有一段时间。在设计程序时应该加入适当的输出条件,满足输出条件即可结束程序。
实际应用
问题提出
有一个且窃贼在偷窃一家商店时发现有10件商品,且每一件的商品的价格和重量均是已知的。小偷希望带走的东西总价值越高越好,但他能带走的商品重量受到背包的限制,最多只能装下50磅东西。若是想要收益最大化,该带走哪些商品呢?
附:
商品的价格:34 32 56 67 54 32 45 56 46 70
商品的重量:8 12 24 16 6 9 35 21 18 19
问题分析
设共有N件商品,第i件商品价格为vi,重量为wi,所处状态为xi,其中x的值为0和1,1为被带走,0为未带走。
这个问题具体的数学模型为
其中,价格、重量均是已知的,未知的则是物品是否被带走。对于这种情况,只需要用退火算法对x进行多次取值即可。
解决方案
编程语言我选择的是MATLAB,代码如下:
%% 开始clcclearclose all%% 参数准备a=0.95; %温度衰减速度times=1000; %循环次数k=[34 32 56 67 54 32 45 56 46 70]; %物品价值d=[8 12 24 16 6 9 35 21 18 19]; %物品重量restriction=50; %背包能够承受的最大重量t0=97;tf=3;t=t0; %温度设置num=length(k); %物品数量sol_new=round(rand(1,num)); %随机生成初始解E_current=inf;E_best=inf; %E_current是当前解对应的目标函数,E_best是最优解,E_new是新解的目标函数值%% 模拟退火while t>tf for i=1:times sol_new=chage(sol_new,num,d,restriction); end %计算价值 E_new=sol_new*(-k'); if E_new E_current=E_new; sol_current=sol_new; if E_new E_best=E_new; sol_best=sol_new; end else if Metropolis(E_new,E_current,t) E_current=E_new; sol_current=sol_new; else sol_new=sol_current; end end t=t*a; %温度衰减end%% 输出disp('最优解为')sol_bestdisp('物品总价值为')-E_bestdisp('背包中物品总重量')sol_best*d'
function [sol_new] = chage(sol_new,num,d,restriction)%对x进行随机扰动并使其满足约束条件 %产生随机扰动 temp1=ceil(rand*num); sol_new(1,temp1)=~sol_new(1,temp1); %检查是否满足约束 while (1) s=(sol_new*d'>restriction); if s %如果不满足约束随机放弃一个物品 temp2=find(sol_new==1); temp3=ceil(rand*length(temp2)); sol_new(temp2(temp3))=~sol_new(temp2(temp3)); else break end endend
function [p] = Metropolis(E_new,E_current,t0)%Metropolis准则f=exp( ( E_new-E_current )./t0 );a=rand;if a>=f p=1;else p=0;end
初次尝试,若有不足,望能指正
愣着干嘛,快点关注( ̄▽ ̄)~*