目录
一、网络原理
Hopfield神经网络(HNN)是一种全互联反馈神经网络,它的每一个神经元都和其他神经元连接。
Hopfield神经网络主要分为离散型Hopfield神经网络(DHNN)和连续型Hopfield神经网络(CHNN),离散型Hopfield神经网络中的神经元与生物神经元的差别较大,因为生物神经元的输入与输出是连续的,并且存在时滞,连续型Hopfield神经网络是一种连续时间神经网络模型,在这种网络中神经元的状态可以取0到1之间的任一实数值。因为Hopfield神经网络的网络联想记忆过程就是非线性动力学系统朝某个稳定状态运行的过程,按照动力学规则改变神经元的状态,最终会使神经网络处于某个稳定状态。
离散型Hopfield神经网络是一种多输入多输出代阈值的二态非线性动力学系统,连续型Hopfield神经网络在简化了生物神经元性质的同时,又准确的保留了生物神经网络的动态和非线性特征。根据其特性,离散型Hopfield神经网络通常被用来处理联想记忆问题,连续型Hopfield神经网络通常被用来处理组合优化问题。TSP问题为典型的组合优化问题,因此经过综合判定,选择连续型Hopfield神经网络作为本题算法模型。
利用神经网络解决优化问题的关键就是如何把待求解的优化问题映射为一个神经网络,一般可以将求解问题的每一个可行解用换位矩阵表示。另一个关键问题是构造能量函数,使其最小值对应问题的最优解,它决定了一个特定问题是否能用神经网络解决。通常采用优化理论中的拉格朗日函数和子乘法或者罚函数法来构造能量函数(公式(1))。
| (1) |
式中 是违背约束条件的惩罚函数,
是优化的目标函数,
和
为平衡
和
在总能量函数中的作用的比例常数,且如果最小化
,则
;如果最大化
,则
;。
在TSP问题中能量函数往往要体现该路径的长度,若下标x,y表示城市,i表示第i次访问,则路径长度可以表示为下列一般形式:
| (2) |
式中 表示换位矩阵中第x行第i列的元素,且其值为1时表示第i步访问城市x,其值为0时表示第i步不访问城市x。
在换位矩阵中每行每列都只能有一个元素为1,其余都为0,否则它表示一条无效的路径。每列只有一个元素为1表示每次只经过一个城市(公式(3)),每行只有一个元素为1表示每个城市经过且只经过一次(公式(4))。
| (3) |
| (4) |
利用罚函数法,将上述约束优化问题表示为下列无约束优化问题:
| (5) |
此时Hopfield神经网络的动态方程为:
| (6) |
则=采用一阶欧拉法计算时刻的输入为:
| (7) |
为了保证收敛于正确解,即换位矩阵V每行每列只有一个元素为1,其余为0,应用Sigmoid函数计算。
| (8) |
求解上式,直到收敛,即可得到神经网络的稳态解。
二、算法步骤
Hopfield神经网络解决TSP问题算法步骤如下:
Step 1:设置初始值,初始电压,罚参数A和D,迭代次数T;
Step 2:计算N个城市之间的距离;
Step 3:在0附近设置神经网络输入 的初始化数值;
Step 4:根据动态方程(公式(6))计算;
Step 5:采用一阶欧拉法计算;
Step 6:计算输出换位矩阵;
Step 7:应用公式(5),计算能量函数J ;
Step 8:路径合法性的检查,根据迭代次数判断是否结束,如果结束则终止,否则返回Step 4。
三、代码实现 (Java)
Hopfield类:主要用于模块和接口设计
package Hopfield;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import jxl.NumberCell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
public class hopfield {
private static final double R = 6371.004;
private int cityNum;//城市数量
private int MAX;//迭代次数
//private double bestLength;
private int[] bestTour;
private double[][] distance;//距离矩阵
//private double[][] time;//时间矩阵
double[] xx;//用来输出最佳路径每个城市位置
double[] yy;//用来输出最佳路径每个城市位置
private String filepath;
private double A,D;
private double U0;
//private double[][][][] W;//权值矩阵
private double[][] deltaU;//动态结果
private double[][] V;//换位矩阵
private double[][] Ut;//下一刻输入
private double[][] U;//初始值
private double step;//步长,即delta t
private double E;//能量值
public hopfield(int MAX,int cityNum,double U0,double step,double A,double D,String filepath) {
this.MAX = MAX;
this.cityNum = cityNum;
this.U0 = U0;
this.step = step;
this.A = A;
this.D = D;
this.filepath = filepath;
bestTour = new int [cityNum];
distance = new double[cityNum][cityNum];
W = new double[cityNum][cityNum][cityNum][cityNum];
V = new double[cityNum][cityNum];
deltaU = new double[cityNum][cityNum];
Ut = new double[cityNum][cityNum];
U = new double[cityNum][cityNum];
xx = new double[cityNum];
yy = new double[cityNum];
}
private static double rad(double d) {
return d * Math.PI / 180.0;
}
//计算距离矩阵
public double[][] Distance() throws BiffException, IOException{
double[] x = new double[cityNum];
double[] y = new double[cityNum];
distance = new double[cityNum][cityNum];
//创建输入流
InputStream stream = new FileInputStream(filepath);
//获取Excel文件对象
Workbook rwb = Workbook.getWorkbook(stream);
//获取文件的指定工作表 默认的第一个
Sheet sheet = rwb.getSheet("Sheet2");
for(int i=0;i<sheet.getRows();i++) {
NumberCell cell = null;//解决了Cell只保留三位小数的问题
cell = (NumberCell) sheet.getCell(0,i);
x[i] = cell.getValue();
xx[i] = x[i];
cell = (NumberCell) sheet.getCell(1,i);
y[i] = cell.getValue();
yy[i] = y[i];
}
//计算每个城市间距离
for (int i = 0; i < cityNum - 1; i++) {
distance[i][i] = 0; // 对角线为0
//将经纬度转换为平面距离
for (int j = i + 1; j < cityNum; j++) {
double rij = 2*R*Math.asin(Math.sqrt(Math.pow(Math.sin((rad(y[j])-rad(y[i]))/2), 2) + Math.cos(rad(y[j]))*Math.cos(rad(y[i]))*Math.pow(Math.sin((rad(x[j])-rad(x[i]))/2), 2)));
distance[i][j] = rij;
distance[j][i] = distance[i][j];
}
}
distance[cityNum - 1][cityNum - 1] = 0;
return distance;
}
//神经网络动态方程
public double[][] calc_du(double[][] V,double[][] distance) {
double[] a1 = new double[cityNum];
double[] a2 = new double[cityNum];
double[] b = new double[cityNum];
double[][] t1 = new double[cityNum][cityNum];
double[][] t2 = new double[cityNum][cityNum];
double[][] d = new double[distance.length][V[0].length];
//按列相加
for(int x=0;x<cityNum;x++) {
for(int i=0;i<cityNum;i++) {
a1[x] += V[x][i];
}
}
for(int i=0;i<cityNum;i++) {
for(int j=0;j<cityNum;j++) {
t1[i][j] = a1[j];
}
}
//按行相加
for(int y=0;y<cityNum;y++) {
for(int i=0;i<cityNum;i++) {
a2[i] += V[i][y];
}
}
for(int i=0;i<cityNum;i++) {
for(int j=0;j<cityNum;j++) {
t2[j][i] = a2[j];
}
}
//将第一列放在最后一列
for(int i=0;i<cityNum;i++) {
b[i] = V[i][0];
}
for(int i=0;i<cityNum;i++) {
for(int j=0;j<cityNum-1;j++) {
V[i][j] = V[i][j+1];
}
}
for(int i=0;i<cityNum;i++) {
V[i][cityNum-1] = b[i];
}
double sum=0;
for(int j=0;j<V[0].length;j++) {
for(int k=0;k<distance.length;k++) {
for(int i=0;i<distance[0].length;i++) {
sum=sum+distance[k][i]*V[i][j];
}
d[k][j]=sum;
sum=0;
}
}
for(int i=0;i<cityNum;i++) {
for(int j=0;j<cityNum;j++) {
deltaU[i][j] = -1*A*((t1[i][j]-1)+(t2[i][j]-1))-D*d[i][j];
}
}
return deltaU;
}
//计算输出方程
public double[][] calc_V(double[][] U) {
for(int x=0;x<cityNum;x++) {
for(int i=0;i<cityNum;i++) {
V[x][i] = (1+Math.tanh(U[x][i]/U0))/2;
}
}
return V;
}
//状态更新:下一刻状态Uxi(t0+step)
public double[][] calc_Ut(double[][] deltaU,double[][] U) {
for(int x=0;x<cityNum;x++) {
for(int i=0;i<cityNum;i++) {
Ut[x][i] = deltaU[x][i]*step + U[x][i];
}
}
return Ut;
}
//能量公式
public double calc_E(double[][] V,double[][] dis) {
double[] a1 = new double[cityNum];
double[] a2 = new double[cityNum];
double[] b = new double[cityNum];
double J;
double t1=0,t2=0,t3=0;
double[][] V1 = new double[cityNum][cityNum];
double[][] d = new double[distance.length][V[0].length];
//按列相加
for(int x=0;x<cityNum;x++) {
for(int i=0;i<cityNum;i++) {
a1[x] += V[x][i];
}
}
for(int i=0;i<cityNum;i++) {
t1 += (a1[i]-1)*(a1[i]-1);
}
//按行相加
for(int y=0;y<cityNum;y++) {
for(int i=0;i<cityNum;i++) {
a2[i] += V[i][y];
}
}
for(int i=0;i<cityNum;i++) {
t2 += (a2[i]-1)*(a2[i]-1);
}
//将第一列放在最后一列
for(int i=0;i<cityNum;i++) {
b[i] = V[i][0];
}
for(int i=0;i<cityNum;i++) {
for(int j=0;j<cityNum-1;j++) {
V1[i][j] = V[i][j+1];
}
}
for(int i=0;i<cityNum;i++) {
V1[i][cityNum-1] = b[i];
}
double sum=0;
for(int j=0;j<V[0].length;j++) {
for(int k=0;k<distance.length;k++) {
for(int i=0;i<distance[0].length;i++) {
sum=sum+distance[k][i]*V1[i][j];
}
d[k][j]=sum;
sum=0;
}
}
for(int j=0;j<V[0].length;j++) {
for(int k=0;k<distance.length;k++) {
for(int i=0;i<distance[0].length;i++) {
t3 += d[k][i]*V[i][j];
}
}
}
J = (A*t1+A*t2+D*t3)/2;
return J;
}
public int[] getpath(double[][] V) {
double[] max = new double[cityNum];
//每列最大值
for(int j=0;j<cityNum;j++) {
double a=0;
for(int i=0;i<cityNum;i++) {
if(V[i][j] > a) {
a = V[i][j];
}
}
max[j] = a;
//System.out.print(max[j]+",");
}
int j=0;
for(int i=0;i<cityNum;i++) {
if(max[j] == V[i][j]) {
bestTour[j] = i;
j++;
break;
}
}
return bestTour;
}
public static Object[] szqc(int[] a) {
List list = new ArrayList();
//遍历数组往集合里存元素
for(int i=0;i<a.length;i++){
//如果集合里面没有相同的元素才往里存
if(!list.contains(a[i])){
list.add(a[i]);
}
}
//toArray()方法会返回一个包含集合所有元素的Object类型数组
Object[] newArr = list.toArray();
return newArr;
}
public void solve() throws BiffException, IOException {
int tt[] = new int[cityNum];
distance = Distance();
//初始化Uxi
System.out.println("U");
for(int x=0;x<cityNum;x++) {
for(int i=0;i<cityNum;i++) {
U[x][i] = 1/2*U0*Math.log(cityNum - 1) + (Math.random()*2-1)/10;
System.out.print(U[x][i]+"\t");
}
System.out.println();
}
//初始化输出状态
System.out.println("V");
V = calc_V(U);
for(int x=0;x<cityNum;x++) {
for(int i=0;i<cityNum;i++) {
System.out.print(V[x][i]+"\t");
}
System.out.println();
}
//开始迭代
for(int i=0;i<MAX;i++) {
deltaU = calc_du(V,distance);
Ut = calc_Ut(deltaU, U);
U = Ut;
V = calc_V(U);
for(int x=0;x<cityNum;x++) {
for(int i1=0;i1<cityNum;i1++) {
V[x][i1]=Math.round(V[x][i1]);
}
}
E = calc_E(V,distance);
bestTour = getpath(V);
Object[] tour = szqc(bestTour);
if(tour.length == bestTour.length) {
for(int i1=0;i1<cityNum;i1++) {
//int x = bestTour[i];
System.out.print(bestTour[i1]+",");
tt[i1] = bestTour[i1];
}
System.out.println();
double sum1=0;
for(int i1=0;i1<cityNum-1;i1++) {
sum1 +=distance[bestTour[i1]][bestTour[i1+1]];
}
sum1 += distance[bestTour[0]][bestTour[cityNum-1]];
System.out.println("距离:"+sum1);
System.out.println("E:"+E);
}
}
for(int i=0;i<cityNum;i++) {
System.out.print(tt[i]+",");
}
}
}
hopmain类:调用启动程序
package Hopfield;
import java.io.IOException;
import jxl.read.biff.BiffException;
public class hopmain {
public static void main(String[] args) throws BiffException, IOException {
//依次输入迭代次数,城市数量,U0,步长,以及A、D参数值,文件路径
hopfield hop =new hopfield(50000, 38, 0.02, 0.0001, 1.5, 0.5 ,"D:\\2.xls");
hop.solve();
}
}
四、所用jar包
jxl包是一款专门用来读取Excel文件的工具包,有需要的可以在maven官网直接下载使用。