引言:
我们在前面谈到动态规划的挖金矿问题中, 使用了建模的方式来解题。我们解题时使用的是穷举解空间的方式来解,但是当问题规模很大时,穷举法无法在段时间内求出最优解,此时我们可以找出一个平衡的局部解。
大家可以先看下我前面java实现的挖金矿模型
1.题目描述
有一个国家,所有的国民都非常老实憨厚,某天他们在自己的国家发现了十座金矿,并且这十座金矿在地图上排成一条直线,
国王知道这个消息后非常高兴,他希望能够把这些金子都挖出来造福国民,
首先他把这些金矿按照在地图上的位置从西至东进行编号,
依次为0、1、2、3、4、5、6、7、8、9,然后他命令他的手下去对每一座金矿进行勘测,
以便知道挖取每一座金矿需要多少人力以及每座金矿能够挖出多少金子,然后动员国民都来挖金子。
题目补充说明:
1)挖每一座金矿需要的人数是固定的,多一个人少一个人都不行。国王知道每个金矿各需要多少人手.
2)每一座金矿所挖出来的金子数是固定的.
3)开采一座金矿的人完成开采工作后,他们不会再次去开采其它金矿,因此一个人最多只能使用一次.
详细分析过程看 挖金矿模型
2.测试数据
//分别为挖矿总人数,金矿数
100 5
//下面为详细的挖第i个金矿需要的人数和该金矿对应的金子数
77 92
22 22
29 87
50 46
99 90
3.金矿问题建模(解大规模问题时十分常见)
1)建模:
设能挖出的总金子数为y
g(i)代表第i个金矿挖出的金子数, x(i)代表第i 个金矿挖不挖,x(i)取值为0,1
约束(1)表示挖金矿的人数不能超过总人数这个限制
这个问题就转化为求约束条件下的解,我们可以使用模拟退火算法来求近似解。
4.模拟退火算法原理
1)算法介绍:
模拟退火法是由 Metropolis 于1953 年提出的,是一种基于热力学原理的随机搜索算法,是局部搜索算法的拓展。它与局部搜索算法的不同之处在于:它以一定的概率选择邻域中目标函数值差的状态。
退火是一种物理过程,一种金属物体在加热至一定的温度后,它的所有分子在其状态空间中自由运动。随着温度的下降,这些分子逐渐停留在不同的状态。在温度最低时,分子重新以一定的结构排列。统计力学的研究表明,在同一个温度,分子停留在能量最小的状态的概率比停留在能量大的状态的概率要大。当温度相当高时,每个状态的概率基本相同,都接近平均值。当温度趋向0时,分子停留在最低能量状态的概率趋向于1。
为了克服局部搜索算法极易陷入局部最优解的缺点,模拟退火算法使用基于概率的双方向随机搜索技术:当基于邻域的一次操作使当前解的质量提高时,模拟退火算法接受这个被改进的解作为新的当前解;在相反的情况下,算法以一定的概率exp(-ΔC/T)接受相对于当前解来说质量较差的解作为新的当前解,其中Δc为邻域操作前后解的评价值的差,T为退火过程的控制参数(即温度)。模拟退火算法已在理论上被证明是一种以概率1收敛于全局最优解的全局优化算法。
2)模拟退火算法步骤:
5.模拟退火解决挖金矿问题
import java.util.ArrayList;
import java.util.Random;
public class smulateFire {
int max_n=5;//总金矿数
int max_people=100;//总人数
int peopleNeed[]={77,22,29,50,99};
int gold[]={92,22,87,46,90};
//初始化x,分别代表第i座金矿是否挖,0代表不挖,1代表挖
int x[]=new int[]{0,0,0,0,0};
//当前金矿挖取信息
int currentx[]=new int[5];
//取x时的金子数
int xEvaluation=0;
int currentEvaluation=0;
int bestEvaluation=-1;
int bestX[]=new int[5];
int T=50;//降温次数
int bestT=0;//最好的温度
int N=100;//迭代步数
private float a=0.98f;// 降温系数
private float t0=100f;// 初始温度
Random random=new Random(System.currentTimeMillis());
//评价函数
public int evaluate(int[]x,int g[]){
return getMatrix(x,g);
}
//初始化xi
public void initX(){
for(int i=0;i100){
initX();
}
}
//取x附近的点
public void updateX(int []x,int[] currentx){
copyArray(x,currentx);
while(true){
int ran1=random.nextInt(65535)%max_n;
currentx[ran1]=(currentx[ran1]==1?0:1);
//满足条件则令tempx取x附近的值域
if(getMatrix(currentx,peopleNeed)<=100&&!isEqualX(x,currentx))
break;
}
}
//判断两个点是否重合
public boolean isEqualX(int[]x,int currentx[]){
for(int i=0;ibestEvaluation){
bestEvaluation=currentEvaluation;
copyArray(currentx,bestX);
bestT=k;
}
//评价函数的变化值
int delt=currentEvaluation-xEvaluation;
//如果评价函数值更大则进行更新,否则以一定概率更新
//由于我们这里求的是最大值,所以exp((delt)/t)
if(delt>0||Math.exp((delt)/ t) > random.nextFloat()){
//将当前currentx保存至x中,x代表前一个状态,currentx代表当前状态,并更新当前评价函数值
copyArray(currentx,x);
xEvaluation=currentEvaluation;
}
n+=1;
}
t=a*t;
k+=1;
}
//输出打印
System.out.println("最佳长度");
System.out.println(bestEvaluation);
System.out.println("最佳路径:");
for (int i = 0; i < max_n; i++) {
System.out.print(bestX[i] + ",");
}
}
public static void main(String[] args) {
smulateFire d=new smulateFire();
d.solve();
}
}
执行结果:
最佳长度
133
最佳路径:
0,0,1,1,0,
我们可以看到模拟退火方法在解这类问题时的优势,尤其是解大规模数据问题时,单单使用穷举方法无法在有限时间内找到全局最优解。
但是模拟退火在受限时间内得出一个比较优的局部解。
6.总结
模拟退火原理类似与爬坡算法,但是在爬坡算法上加了一些随机性,模拟了物理中的高温到低温的一种渐变的趋于稳定的过程。