模拟退火算法

落花浮王杯 提交于 2019-12-28 12:19:07

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();
    }
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!