1.算法原理
寻找f(x)最大值的问题,爬山算法搜索到局部最高点A点时就会停止搜索,原因是A点左右的值均小于A点的值。模拟退火算法采用的解决办法是以一定的概率选择A两边的点,尽管A两边的点并不是局部最优解,这样就有一定的概率搜索到D点,从而搜索到全局最高点B点,最终获得了全局最优解。
上文中的一定概率来自于固体退火原理:当固体温度较高时,物质内能较大,固体内部分子运动剧烈;当温度逐渐降低时,物体内能也随之降低,分子运动趋于平稳;当固体温度降到常温时,固体内部分子运动最终平稳。根据Metropolis准则,粒子在温度T时趋于平衡的概率为e^(-ΔE/(kT)),其中E为温度T时的内能,ΔE为其改变量,k为Boltzmann常数(推荐缓慢降温数值:0.95-0.99)。
2.算法步骤
3.算法实现
TSP问题
package com.suyue.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/*
@Project:Optimization
@Package:
@Date:16:32
@User:15616
@Problem:TSP
Solution:Simulated Annealing Algorithm
*/
public class SimulatedAnnealingAlgorithm {
private int cityCount=31;
private double T=5000; //初始温度
private double endT=1e-8; //最终温度
private int L=100; //每个温度下的迭代次数
private double delta=0.98; //迭代系数
private int[][] cities; //城市坐标[cityCount][2]
private double[][] distance; //距离表
private int[] ans; //解决路径
private int cnt=0; //降温次数
private double ansDistance=0;
private List<int[]> process_ansPath; //优化过程
public void calDistance(){
for(int i=0;i<cityCount;++i)
for(int k=0;k<cityCount;++k){
distance[i][k]=Math.sqrt(Math.pow((double)(cities[i][0]-cities[k][0]),2)+Math.pow((double)(cities[i][1]-cities[k][1]),2));
}
}
public void saa(){
process_ansPath=new ArrayList<int[]>();
calDistance();
for(int i=0;i<cityCount;++i) ans[i]=i; //初始化一条路径
process_ansPath.add(Arrays.copyOf(ans,cityCount));
double t=T; //当前温度
int[] ans_new=new int[cityCount]; //新路径
double d1,d2,d3; //原路径、新路径的距离、距离差
while (t>=endT){
for(int i=0;i<L;++i){
ans_new=exchange2CitiesPath(ans); //随机交换两个城市以产生新的路径
d1=getPathDistance(distance,ans);
d2=getPathDistance(distance,ans_new);
d3=d2-d1;
ansDistance=d1;
if(d3<0||Math.exp(-d3/t)>(new Random()).nextDouble()){
ans=Arrays.copyOf(ans_new,cityCount);
ansDistance=d2;
}
}
process_ansPath.add(Arrays.copyOf(ans,cityCount));
t*=delta;
++cnt;
}
}
public int getCityCount() {
return cityCount;
}
public void setCityCount(int cityCount) {
this.cityCount = cityCount;
}
public double getT() {
return T;
}
public void setT(double t) {
T = t;
}
public double getEndT() {
return endT;
}
public void setEndT(double endT) {
this.endT = endT;
}
public int getL() {
return L;
}
public void setL(int l) {
L = l;
}
public double getDelta() {
return delta;
}
public void setDelta(double delta) {
this.delta = delta;
}
public int[][] getCities() {
return cities;
}
public void setCities(int[][] cities) {
for(int i=0;i<cities.length;++i)
this.cities[i]= Arrays.copyOf(cities[i],cities[i].length);
}
public double getAnsDistance(){
return ansDistance;
}
public void setAnsDistance(){
this.ansDistance=getPathDistance(distance,ans);
}
public int[] getAns(){
return ans;
}
public int getCnt(){
return cnt;
}
public List<int[]> getProcess_ansPath(){
return process_ansPath;
}
public void defaultInitializationData(){
setCityCount(31);
setT(5000);
setEndT(1e-8);
setL(100);
setDelta(0.98);
cities=new int[cityCount][2];
int[][] tempCities={{1304,2312},{3639,1315},{4177,2244},{3712,1399},{3488,1535},{3326,1556},{3238,1229},{4196,1004},
{4312,790},{4386,570},{3007,1970},{2562,1756},{2788,1491},{2381,1676},{1332,695},{3715,1678},{3918,2179},
{4061,2370},{3780,2212},{3676,2578},{4029,2838},{4263,2931},{3429,1908},{3507,2367},{3394,2643},{3439,3201},
{2935,3240},{3140,3550},{2545,2357},{2778,2826},{2370,2975}};
setCities(tempCities);
distance=new double[cityCount][cityCount];
ans=new int[cityCount];
cnt=0;
}
public void randomInitializationData(int n,int startT,int blockLength,double deltaK){
cityCount=n;
T=startT;
endT=1e-8;
L=blockLength;
delta=deltaK>=1.0 ? 0.98 : deltaK;
cities=new int[cityCount][2];
Random random=new Random();
for(int i=0;i<cities.length;++i)
for(int k=0;k<cities[i].length;++k)
cities[i][k]=random.nextInt(5000);
distance=new double[cityCount][cityCount];
ans=new int[cityCount];
cnt=0;
}
public static int[] exchange2CitiesPath(int[] path){
Random random=new Random();
int i=random.nextInt(path.length);
int k=random.nextInt(path.length);
int temp=path[i];
path[i]=path[k];
path[k]=temp;
return path;
}
public static double getPathDistance(double[][] d,int[] path){
double dis=0;
for(int i=0;i<path.length-2;++i) dis+=d[path[i]][path[i+1]];
return dis;
}
public static void main(String[] args) { //test
SimulatedAnnealingAlgorithm simulatedAnnealingAlgorithm=new SimulatedAnnealingAlgorithm();
//simulatedAnnealingAlgorithm.randomInitializationData(50,5000,100,0.98);
simulatedAnnealingAlgorithm.defaultInitializationData();
long startTime=System.currentTimeMillis();
simulatedAnnealingAlgorithm.saa();
long endTime=System.currentTimeMillis();
System.out.println("saa算法解决"+simulatedAnnealingAlgorithm.getCityCount()+"城TSP问题所需时间:"+(endTime-startTime)+"ms");
System.out.println("路径如下:\n"+Arrays.toString(simulatedAnnealingAlgorithm.getAns()));
System.out.println("路径长度为:"+simulatedAnnealingAlgorithm.getAnsDistance());
System.out.println("降温次数为:"+simulatedAnnealingAlgorithm.getCnt());
System.out.println("城市坐标为:" );
for(int i=0;i<simulatedAnnealingAlgorithm.getCityCount();++i){
System.out.print(Arrays.toString(simulatedAnnealingAlgorithm.getCities()[i])+" ");
if(i%10==9) System.out.println();
}
System.out.println("降温过程为:");
for (int i=0;i<simulatedAnnealingAlgorithm.getProcess_ansPath().size();++i){
if(i>20) continue; //测试输出前20次降温过程
System.out.println(Arrays.toString(simulatedAnnealingAlgorithm.getProcess_ansPath().get(i)));
}
}
}
package com.suyue.frame;
import com.suyue.core.SimulatedAnnealingAlgorithm;
import com.suyue.frame.panel.SaaPanel;
import org.ietf.jgss.MessageProp;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.TextListener;
class OptimizationFrame extends JFrame{
private SimulatedAnnealingAlgorithm saa=new SimulatedAnnealingAlgorithm();
private Algorithm algorithmSelect=Algorithm.SSA;
private SaaPanel saaPanel;
private JToolBar toolBar;
private JButton startButton;
private JTextField millis;
private JMenuBar menuBar;
private JMenu algorithmSelectMenu,dataSelectMenu;
private JMenuItem saaMenuItem,otherAlgorithmItem,defaultDataItem,randomDataItem,fileDataItem;
private JProgressBar progressBar;
public OptimizationFrame(){
saaPanel=new SaaPanel();
//this.setContentPane(saaPanel);
this.getContentPane().add(saaPanel);
createFrameMenu();
createFrameTool();
createProgressBar();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.setSize(1000,800);
this.setLocationRelativeTo(null);
}
protected void createFrameMenu(){
menuBar=new JMenuBar();
algorithmSelectMenu=new JMenu("算法选择");
saaMenuItem=new JMenuItem("SAA算法");
otherAlgorithmItem=new JMenuItem("其它...");
algorithmSelectMenu.add(saaMenuItem);
algorithmSelectMenu.addSeparator();
algorithmSelectMenu.add(otherAlgorithmItem);
menuBar.add(algorithmSelectMenu);
dataSelectMenu=new JMenu("数据输入");
defaultDataItem=new JMenuItem("默认输入");
randomDataItem=new JMenuItem("随机填充数据");
fileDataItem=new JMenuItem("从文件导入...");
dataSelectMenu.add(defaultDataItem);
dataSelectMenu.add(randomDataItem);
dataSelectMenu.add(fileDataItem);
menuBar.add(dataSelectMenu);
dataSelectMenu.setEnabled(false);
saaMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
algorithmSelect=Algorithm.SSA;
dataSelectMenu.setEnabled(true);
}
});
otherAlgorithmItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
algorithmSelect=Algorithm.OTHER_ALGORITHM;
dataSelectMenu.setEnabled(false);
}
});
defaultDataItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
saa.defaultInitializationData();
startButton.setEnabled(true);
}
});
randomDataItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFrame frame=new JFrame("请输入以下参数...");
JLabel cityCountLabel=new JLabel("城市数量(n:");
JTextField cityCount=new JTextField();
JLabel originalTemperatureLabel=new JLabel("初始温度(T");
JTextField originalTemperature=new JTextField();
JLabel blockLengthLabel=new JLabel("步长(l:");
JTextField blockLength =new JTextField();
JLabel deltaLabel=new JLabel("降温系数(0<delta<1:");
JTextField delta=new JTextField();
JPanel panel=new JPanel();
panel.setLayout(new GridLayout(4,2));
panel.add(cityCountLabel);
panel.add(cityCount);
panel.add(originalTemperatureLabel);
panel.add(originalTemperature);
panel.add(blockLengthLabel);
panel.add(blockLength);
panel.add(deltaLabel);
panel.add(delta);
JPanel originalPanel=new JPanel();
originalPanel.setLayout(new BorderLayout());
originalPanel.add(panel,BorderLayout.NORTH);
JButton button=new JButton("确认");
originalPanel.add(button,BorderLayout.SOUTH);
frame.setContentPane(originalPanel);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int n=Integer.parseInt(cityCount.getText());
int temperature=Integer.parseInt(originalTemperature.getText());
int blockL=Integer.parseInt(blockLength.getText().trim());
saa.randomInitializationData(n,temperature,blockL,Double.parseDouble(delta.getText().trim()));
frame.dispose();
startButton.setEnabled(true);
}
});
frame.setVisible(true);
frame.setSize(300,240);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
}
});
this.setJMenuBar(menuBar);
}
protected void createFrameTool(){
toolBar=new JToolBar("工具栏");
startButton=new JButton("开始");
startButton.setEnabled(false);
toolBar.setFloatable(false);
toolBar.add(startButton);
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
saa.saa();
saaPanel.setCoordinate(saa.getCities());
saaPanel.setPath(saa.getProcess_ansPath());
saaPanel.display();
}
});
millis=new JTextField("100");
millis.setSize(50,28);
toolBar.addSeparator();
toolBar.add(millis);
millis.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
changedUpdate(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
changedUpdate(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
if(millis.getText().length()>=5) millis.setText("100");
long m=Long.parseLong(millis.getText().trim());
if(m<10||m>10000) m=100;
//millis.setText(String.valueOf(m));
saaPanel.setMillis(m);
}
});
this.add(toolBar, BorderLayout.NORTH);
}
protected void createProgressBar(){
progressBar=new JProgressBar();
progressBar.setStringPainted(true);
progressBar.setBackground(Color.black);
new Thread(()->{
while (true) {
System.out.println("代数:"+saaPanel.getIPath());
if(saaPanel!=null&&saaPanel.getIPath()!=0){
System.out.println("代数:"+saaPanel.getIPath());
progressBar.setString("正在绘制算法过程...0%");
while (saaPanel.getIPath() != saaPanel.getPath_Process_Size()) {
System.out.println("代数:"+saaPanel.getIPath());
progressBar.setValue(saaPanel.getIPath() * 100 / saaPanel.getPath_Process_Size());
progressBar.setString("正在绘制算法过程..."+progressBar.getValue()+"%");
System.out.println(progressBar.getValue());
try {
Thread.sleep(saaPanel.getMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
progressBar.setValue(100);
progressBar.setString("算法绘制完成...");
}).start();
this.getContentPane().add(progressBar,BorderLayout.SOUTH);
}
public static void main(String[] args) {
OptimizationFrame optimizationFrame=new OptimizationFrame();
}
}
enum Algorithm{
SSA,OTHER_ALGORITHM
}
package com.suyue.frame.panel;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/*
@Project:Optimization
@Package:
@Date:9:19
@User:15616
*/
public class SaaPanel extends JPanel {
private int[][] path_Process;
private int[][] coordinate;
private int iPath=0;
private boolean isStart=false;
private long millis=100; //刷新间隔,默认100ms
@Override
public void paint(Graphics g) {
super.paint(g);
//System.out.println("paint"+this.getHeight()+","+this.getWidth());
if(isStart){
int temp=(iPath++)%path_Process.length;
int[] path=Arrays.copyOf(path_Process[temp],path_Process[temp].length);
Graphics2D g2D=(Graphics2D)g;
g2D.setColor(Color.BLUE);
int w=this.getWidth(),h=this.getHeight();
for(int i=0;i<path.length-1;++i){
g2D.drawLine(coordinate[path[i]][0]*w/5000,coordinate[path[i]][1]*h/5000,coordinate[path[i+1]][0]*w/5000,coordinate[path[i+1]][1]*h/5000);
g2D.fillOval(coordinate[path[i]][0]*w/5000,coordinate[path[i]][1]*h/5000,5,5);
g2D.drawString(String.valueOf(path[i]),coordinate[path[i]][0]*w/5000-4,coordinate[path[i]][1]*h/5000-4);
}
}
}
public void setCoordinate(int[][] c) {
coordinate=new int[c.length][c[0].length];
for(int i=0;i<c.length;++i) coordinate[i]=Arrays.copyOf(c[i],c[i].length);
}
public void setPath(List<int[]> p) {
path_Process=new int[p.size()][p.get(0).length];
for(int i=0;i<p.size();++i) path_Process[i]=Arrays.copyOf(p.get(i),p.get(i).length);
}
public int getIPath() {
return iPath;
}
public int getPath_Process_Size(){
return path_Process.length;
}
public void setMillis(long millis) {
this.millis = millis;
}
public long getMillis() {
return millis;
}
public void display() {
isStart=true;
new Thread(() -> {
while (iPath!=path_Process.length){
try {
Thread.sleep(millis);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
repaint();
}
iPath=0;
}).start();
}
}
来源:CSDN
作者:今朝相守观桑田
链接:https://blog.csdn.net/qq_34262612/article/details/103741091