数学建模算法之模拟退火

数学建模算法之模拟退火

模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。

模拟退火算法的步骤

模拟退火算法新解的产生和接受可分为如下四个步骤:

第一步是由一个产生函数从当前解产生一个位于解空间的新解;

第二步是计算与新解所对应的目标函数差。

第三步是判断新解是否被接受,判断的依据是一个接受准则

第四步是当新解被确定接受时,用新解代替当前解
模拟退火算法在数学建模中多用于求最优化情况(最小值或最大值),典型实例:TSP(旅行商最短路径),0/1背包,快递买书问题中。

这里举例讲解快递买书问题

例:从15个书店里购进20本不同的书,每个书店都有不同的运费,在同一个书店购买不同的书运费不会累计,求最小花费。
数据
在这里插入图片描述

解:我们可以在一个书店购买不同的书而运费只需一次即可。如何选择才能使得我们求出的花费总和最小。如果我们用穷举法的话,次数要15的20次方,无法正常计算。所以我们采用模拟退火算法来求解。

我们先要预定一个解空间,该解空间的长度为要购书数量,将数组随机赋值,每一个值都表示将要在哪一个书店进行购书,如果我们要计算相邻数据的值,只需要随机给一个位置随机赋值,就可以计算出一个新的值。然后比较相邻解的大小和接受概率判断是否接受这个解。并通过控制温度系数和迭代次数控制程序运行的次数就能求出一个最优解(由于给定初始条件不一样会导致算出来的解并不是全局最优解,而是一个局部最优解),请尽可能的多计算几次。
最优解的答案为:466
最优购买方案为6 14 6 6 6 14 11 6 14 6 14 4 11 14 14 6 4 4 14 11

以下给出MATLAB完整代码:

function  money =  sumd_money(way,freight,M,b)
% 输入:way: 购买方案; fright:运费;  M: 每本书在每家店的价格; b:一共要购买几本书
   index = unique(way);  % 在哪些商店购买了商品,因为我们等下要计算运费
   money = sum(freight(index)); % 计算买书花费的运费
    % 计算总花费:刚刚计算出来的运费 + 20本书的售价
    for i = 1:b 
        money = money + M(way(i),i);  
    end
end

function way1 = gen_new_way(way0, s, b)
% way0:原来的买书方案,是一个1*b的向量,每一个元素都位于1-s之间
        index =  randi([1, b],1) ;  % 看哪一本书要更换书店购买
        way1 = way0;  % 将原来的方案赋值给way1
        way1(index) = randi([1, s],1);  % 将way1中的第index本书换一个书店购买   
end

clear; clc   
% 导入书的价格
% 这个数据文件里面保存了两个矩阵:M是每本书在每家店的价格; freight表示每家店的运费
[s, b] = size(M);  % s是书店的数量,b是要购买的书的数量,M为书价矩阵 
T0 = 1000;   % 初始温度
T = T0; % 迭代中温度会发生改变,第一次迭代时温度就是T0
k_total= 1000;  % 最大迭代次数
Lk = 300;  % 每个温度下的迭代次数
yita = 0.95;  % 温度衰减系数
way0 = randi([1, s],1,b); %1-s这些整数中随机抽取一个1*b的向量,表示这b本书分别在哪家书店购买
zhong_way = way0; %用来做中间计算
zhong_money = sumd_money(way0,freight,M,b);%zhong_money用来储存数值,sumd_money为花钱函数计算,在上面可以看到,freight是快递费矩阵

for iter = 1 : k_total  % 外循环, 我这里采用的是指定最大迭代次数
    for i = 1 : Lk  %  内循环,在每个温度下开始迭代
        money0 = sumd_money(way0,freight,M,b); % 调用我们自己写的sum_money函数计算这个方案的花费
        way1 = gen_new_way(way0,s,b);  % 调用我们自己写的gen_new_way函数,也在上面
        money1 = sumd_money(way1,freight,M,b); % 计算新方案的花费
        if money1 < money0    % 如果新方案的花费小于当前方案的花费
            way0 = way1; % 更新当前方案为新方案
            zhong_way = [zhong_way;way1]; % 将新找到的way1添加到中间结果中
            zhong_money = [zhong_money; money1];  % 将新找到的money1添加到中间结果中
        else
            p = exp(-(money1 - money0)/T); % 根据Metropolis准则计算一个概率
            if rand(1) < p   % 生成一个随机数和这个概率比较,如果该随机数小于这个概率
                way0 = way1;  % 更新当前方案为新方案
            end
        end
    end
    T = yita*T;   % 温度下降       
end


[best_money, ind] = min(zhong_money);  % 找到最小的花费的值,以及其的下标
min_way = zhong_way(ind,:); % 根据下标找到此时方案
disp('最佳的方案是:'); disp(min_way)
disp('此时最优值是:'); disp(best_money)
% % 注意:代码仅供参考,不可直接用于自己的数模论文中
% % 国赛对于论文的查重要求非常严格,代码雷同也算作抄袭

猜你喜欢

转载自blog.csdn.net/qq_44858401/article/details/108059290
今日推荐