人工智能实验报告:货郎担问题

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

人工智能实验报告
西安交通大学
实验一:货郎担问题
一、问题重述
假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路经的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。

路径的选择目标是要求得的路径路程为所有路径之中的最小值。

二、问题分析
如果只考虑贪心算法,由于在最后需要回到原来的出发点,很难得到最后的最有结果,所以需要全方面地考虑到各种情况。

本实验由于考虑的村庄的总数量不是很多,可以采取枚举算法。

枚举所有的状况,进而做出比较,可得到全局的最优解。

三、程序设计
程序可以采用sort和work两个函数进行具体的计算,主函数主要用来编辑界面,使得最终的结果比较可视化。

首先,定义dis矩阵作为距离矩阵,定义way数组为路程记录。

对主函数的操作:
1.输出如下编辑的运行界面:
cout<<" ================货郎担问题的解法================"<<endl<<endl<<endl;
cout<<" 1 ---------------输入数据---------------"<<endl;
cout<<" 2 ---------------输出原始数据-----------"<<endl;
cout<<" 3 ---------------解决问题---------------"<<endl;
cout<<" 4 ---------------输出最终结果-----------"<<endl;
cout<<" 0 ---------------退出系统---------------"<<endl<<endl;
cout<<"请选择相应序号:"<<endl;
2.在输入数据选项中,将各个村庄自己和自己之间的值设为0,再设置a村庄与b村庄之间的距离和b村庄和a村庄之间的距离相同。

其他数据依靠外部输入。

3.在输出原始数据选项中,用矩阵的方式做一次确认。

4.在解决问题选项中,调用work函数。

5.在输出最终结果选项中,用cout进行way的输出。

对work函数的操作:
调用sort函数,列出所有的路径情况,求出最小值。

对sort函数的操作:
完成一个范围内的从小到大排列。

四、程序运行结果
先初始化各个数据,在这里假设一共有五个村庄,设定每个村庄之间的距离,以及出发的村庄编号。

输入的各数据显示如下:
输出最后的数据结果(行动方案)如下:
五、源代码
// seller.cpp : 定义控制台应用程序的入口点。

#include"stdafx.h"
#include<iostream>
#include<stdlib.h>
usingnamespace std;
#define numOfVill 5 //常量定义,设置一共有5个村庄
//全局变量定义
int dis[numOfVill][numOfVill]; //定义各个村庄之间的距离矩阵
int Way[numOfVill];
void Sort(int a[],int low,int up);
int Work(int a[],int n,int b);
int main( )
{int m,s,i,j,v[numOfVill],k;
int n=numOfVill;
while(1)
{
cout<<" ================货郎担问题的解法================"<<endl<<endl<<endl;
cout<<" 1 ---------------输入数据---------------"<<endl;
cout<<" 2 ---------------输出原始数据-----------"<<endl;
cout<<" 3 ---------------解决问题---------------"<<endl;
cout<<" 4 ---------------输出最终结果-----------"<<endl;
cout<<" 0 ---------------退出系统---------------"<<endl<<endl;
cout<<"请选择相应序号:"<<endl;
cin>>k;
cout<<endl;
switch(k)
{
case 1:
{
for(i=0;i<n;i++)
v[i]=i;
cout<<"请输入出发村庄的编号: ";
cin>>m;
m=m-1;
getchar();
for(i=m;i<n-1;i++)
v[i]=v[i+1]; //将选中的村庄之后的村庄位置前移//设置距离矩阵
cout<<endl<<"请输入各个村庄之间的距离数据:"<<endl;
for(i=0;i<n;i++)
{
dis[i][i]=0;//村庄自己和自己之间的距离为0
}
for(i=0;i<n;i++)
for(j=i+1;j<n;j++)
{
cout<<"村庄"<<i+1<<"与村庄"<<j+1<<"的距离: ";
cin>>dis[i][j];
dis[j][i]=dis[i][j];//a到b的距离和b到a的距离相同}
cout<<"输入完毕!"<<endl;
system("pause");
system("cls");
break;
}
case 2:
{
cout<<"输入各个村庄的距离如下:"<<endl; //检查输出
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
cout<<dis[i][j]<<"";
cout<<endl;
}
system("pause");
system("cls");
break;
}
case 3:
{
s=Work(v,n-1,m);
cout<<"问题解决完毕!"<<endl;
system("pause");
system("cls");
break;
}
case 4:
{
cout<<"最短路径为"<<endl<<m+1<<"-"; //输出起始地址编号
for(i=0;i<n-1;i++)
cout<<Way[i]+1<<"-"; //输出路径
cout<<m+1;
cout<<"最小值为"<<s<<endl;
system("pause");
system("cls");
break;
}
case 0:
{
cout<<endl<<"谢谢使用!"<<endl;
exit(0);
break;
}
default:
{
cout<<"对不起,您的输入有误……"<<endl;
system("pause");
system("cls");
}
}
}
return 0;
}
void Sort(int a[],int low,int up)//排序,使得a[low]到a[up]之间按照从小到大排列{
int i,j,k,t;
for(i=low+1;i<=up;i++)
for(j=low;j<i;j++)
if(a[j]>a[i])
{
t=a[i];
for(k=i;k>j;k--)
a[k]=a[k-1];
a[j]=t;
break;
}
}
int Work(int a[],int n,int b)//解决问题
{
int i,j,k,t,cost,min;
cost=0;
min=0;
long s,count;
Sort(a,0,n-1);
min+=dis[b][a[0]];
for(i=0;i<n-1;i++)
min+=dis[a[i]][a[i+1]];
min+=dis[a[i]][b];
for(i=0;i<n;i++)
Way[i]=a[i]; //首先求出序号从小到大排列后的距离数
s=1;
for(i=1;i<=n;i++)
s=s*i; //按照排列组合原理,可知一共有n!种方案
for(count=2;count<=s;count++) //第一种已经计算完毕,所以从2开始计数
{
j=n-1;
while(a[j-1]>a[j]) //从后往前找,如果此时前一个数比后一个大,说明在a[j-1]之后已经完成了循环,则需要改变前面的序号,即当序列为0342时,说明第三第四位已经完成了循环,下一次循环需要从第二个位置着手,即需要将序列改为0423
j--;
k=j;
a[k]=a[j];
for(i=j+1;i<n;i++)
if(a[k]>a[i]&&a[i]>a[j-1])
{
k=i;
a[k]=a[i];
}
t=a[k];
a[k]=a[j-1];
a[j-1]=t;
Sort(a,j,n-1); //重新整理顺序
cost+=dis[b][a[0]];
for(i=0;i<n-1;i++)
cost+=dis[a[i]][a[i+1]];
cost+=dis[a[i]][b];
if(cost<min) //用穷举法取得最终值
{
min=cost;
for(i=0;i<n;i++)
Way[i]=a[i];
}
cost=0;
}
return min;
}
实验二:BP神经网络拟合正弦函数
一、问题重述
使用三层的BP神经网络,完成正弦函数拟合的作用。

二、问题分析
用三层的神经网络,采用s型函数。

采用伪代码如下:
For k=1 to L do初始化各层所有权值W(k);
初始化精度控制参数ε;
Begin
E=0;
For 样本集S中的每个样本do
Begin
计算出Xp对应的实际输出Op;
计算出样本误差Ep;
累计样本误差E=E+Ep;
调整输出层(第L层)权值W(L);
For k=L-1 to 0 do
Begin
调整第k层权值W(L);
k=k-1;
End
End
E=E/2.0;
End
三、程序设计
定义常量:
#define innode 1 //输入结点数,即x
#define hidenode 10//隐含结点数
#define outnode 1 //输出结点数,即sin(x)
#define trainsample 11//BP训练样本数
定义变量:
rate_w=0.9; //权值学习率(输入层--隐含层)
rate_w1=0.9; //权值学习率 (隐含层--输出层)
rate_b1=0.9; //隐含层阀值学习率
rate_b2=0.9; //输出层阀值学习率
完成网络权值初始化—>向前转播阶段—>误差转播阶段—>判断是否停止的过程。

其中训练函数使用s型函数调整,并且调整权重。

(再具体的程序设计思想可见源代码注释,不再赘述)
四、程序运行结果
误差在百分之四左右。

五、源代码
// BP.cpp : 定义控制台应用程序的入口点。

#include"stdafx.h"
#include<iostream>
#include<cmath>
#include<fstream>
usingnamespace std;
#define innode 1 //输入结点数,即x
#define hidenode 10//隐含结点数
#define outnode 1 //输出结点数,即sin(x)
#define trainsample 11//BP训练样本数
class BpNet
{
public:
void train(double p[trainsample][innode ],double t[trainsample][outnode]);//Bp训练double p[trainsample][innode]; //输入的样本
double t[trainsample][outnode]; //样本要输出的
double *recognize(double *p);//Bp识别
void writetrain(); //写训练完的权值
void readtrain(); //读训练好的权值,这使的不用每次去训练了,只要把训练最好的权值存下来就OK
BpNet();
virtual ~BpNet();
public:
void init();
void test(double x);
double w[innode][hidenode];//隐含结点权值
double w1[hidenode][outnode];//输出结点权值
double b1[hidenode];//隐含结点阀值
double b2[outnode];//输出结点阀值
double rate_w; //权值学习率(输入层-隐含层)
double rate_w1;//权值学习率 (隐含层-输出层)
double rate_b1;//隐含层阀值学习率
double rate_b2;//输出层阀值学习率
double e;//误差计算
double error;//允许的最大误差
double result[outnode];// Bp输出
};
BpNet::BpNet()
{
error=1.0;
e=0.0;
rate_w=0.9; //权值学习率(输入层--隐含层)
rate_w1=0.9; //权值学习率 (隐含层--输出层)
rate_b1=0.9; //隐含层阀值学习率
rate_b2=0.9; //输出层阀值学习率
}
BpNet::~BpNet()
{
}
void winit(double w[],int n) //权值初始化
{
for(int i=0;i<n;i++)
w[i]=(2.0*(double)rand()/RAND_MAX)-1; //初始权值取小随机数,避免饱和状态,各个权值尽量不相同
}
void BpNet::init() //初始化
{
winit((double*)w,innode*hidenode);
winit((double*)w1,hidenode*outnode);
winit(b1,hidenode);
winit(b2,outnode);
}
void BpNet::train(double p[trainsample][innode],double t[trainsample][outnode])
{
double pp[hidenode];//隐含结点的校正误差
double qq[outnode];//希望输出值与实际输出值的偏差
double yd[outnode];//希望输出值
double x[innode]; //输入向量
double x1[hidenode];//隐含结点状态值
double x2[outnode];//输出结点状态值
double o1[hidenode];//隐含层激活值
double o2[hidenode];//输出层激活值
for(int isamp=0;isamp<trainsample;isamp++)//循环训练一次样品
{
for(int i=0;i<innode;i++)
x[i]=p[isamp][i]; //输入的样本
for(int i=0;i<outnode;i++)
yd[i]=t[isamp][i]; //期望输出的样本
//构造每个样品的输入和输出标准
for(int j=0;j<hidenode;j++)
{
o1[j]=0.0;
for(int i=0;i<innode;i++)
o1[j]=o1[j]+w[i][j]*x[i];//隐含层各单元输入激活值
x1[j]=1.0/(1+exp(-o1[j]-b1[j]));//隐含层各单元的输出,其中b1为阈值,x1是通过f函数的输出,用s型函数
}
for(int k=0;k<outnode;k++)
{
o2[k]=0.0;
for(int j=0;j<hidenode;j++)
o2[k]=o2[k]+w1[j][k]*x1[j]; //输出层各单元输入激活值
x2[k]=1.0/(1.0+exp(-o2[k]-b2[k]));//同样适用s型函数
}
for(int k=0;k<outnode;k++)
{
qq[k]=(yd[k]-x2[k])*x2[k]*(1-x2[k]); //希望输出与实际输出的偏差
for(int j=0;j<hidenode;j++)
w1[j][k]+=rate_w1*qq[k]*x1[j]; //下一次的隐含层和输出层之间的新连接权 }
for(int j=0;j<hidenode;j++)
{
pp[j]=0.0;
for(int k=0;k<outnode;k++)
pp[j]=pp[j]+qq[k]*w1[j][k];
pp[j]=pp[j]*x1[j]*(1-x1[j]); //隐含层的校正误差
for(int i=0;i<innode;i++)
w[i][j]+=rate_w*pp[j]*x[i]; //下一次的输入层和隐含层之间的新连接权 }
for(int k=0;k<outnode;k++)
{
e+=fabs(yd[k]-x2[k])*fabs(yd[k]-x2[k]); //计算均方差
}
error=e/2.0;
for(int k=0;k<outnode;k++)
b2[k]=b2[k]+rate_b2*qq[k]; //下一次的隐含层和输出层之间的新阈值
for(int j=0;j<hidenode;j++)
b1[j]=b1[j]+rate_b1*pp[j]; //下一次的输入层和隐含层之间的新阈值
}
}
double *BpNet::recognize(double *p)
{
double x[innode]; //输入向量
double x1[hidenode]; //隐含结点状态值
double x2[outnode]; //输出结点状态值
double o1[hidenode]; //隐含层激活值
double o2[hidenode]; //输出层激活值
for(int i=0;i<innode;i++)
x[i]=p[i];
for(int j=0;j<hidenode;j++)
{
o1[j]=0.0;
for(int i=0;i<innode;i++)
o1[j]=o1[j]+w[i][j]*x[i]; //隐含层各单元激活值
x1[j]=1.0/(1.0+exp(-o1[j]-b1[j]));
}
for(int k=0;k<outnode;k++)
{
o2[k]=0.0;
for(int j=0;j<hidenode;j++)
o2[k]=o2[k]+w1[j][k]*x1[j];//输出层各单元激活值
x2[k]=1.0/(1.0+exp(-o2[k]-b2[k]));
}
for(int k=0;k<outnode;k++)
{
result[k]=x2[k]; //result记下最后的输出
}
return result;
}
void BpNet::writetrain()
{
FILE *stream0;
FILE *stream1;
FILE *stream2;
FILE *stream3;
int i,j;
//隐含结点权值写入
if((fopen_s(&stream0,"w.txt","w"))==NULL) cout<<"找到文件";
else
{
cout<<"创建文件失败!";
exit(1);
}
for(i=0;i<innode;i++)
{
for(j=0;j<hidenode;j++)
{
fprintf(stream0, "%f\n", w[i][j]); }
}
fclose(stream0);
if((fopen_s(&stream1,"w1.txt","w"))==NULL) cout<<"找到文件";
else
{
cout<<"创建文件失败!";
exit(1);
}
for(i=0;i<hidenode;i++)
{
for(j=0;j<outnode;j++)
{
fprintf(stream1, "%f\n",w1[i][j]); }
}
fclose(stream1);
//隐含结点阀值写入
if((fopen_s(&stream2,"b1.txt","w"))==NULL) cout<<"找到文件";
else
{
cout<<"创建文件失败!";
exit(1);
}
for(i=0;i<hidenode;i++)
fprintf(stream2, "%f\n",b1[i]);
fclose(stream2);
//输出结点阀值写入
if((fopen_s(&stream3,"b2.txt","w"))==NULL) cout<<"找到文件";
else
{
cout<<"创建文件失败!";
exit(1);
}
for(i=0;i<outnode;i++)
fprintf(stream3, "%f\n",b2[i]);
fclose(stream3);
}
void BpNet::readtrain()
{
FILE *stream0;
FILE *stream1;
FILE *stream2;
FILE *stream3;
int i,j;
//隐含结点权值读出
if((fopen_s(&stream0,"w.txt","r"))==NULL)
cout<<"找到文件";
else
{
cout<<"打开文件失败!";
exit(1);
}
float wx[innode][hidenode];
for(i=0;i<innode;i++)
{
for(j=0;j<hidenode;j++)
{
fscanf_s(stream0, "%f", &wx[i][j]); w[i][j]=wx[i][j];
}
}
fclose(stream0);
//输出结点权值读出
if((fopen_s(&stream1,"w1.txt","r"))==NULL)
cout<<"找到文件";
else
{
cout<<"打开文件失败!";
exit(1);
}
float wx1[hidenode][outnode];
for(i=0;i<hidenode;i++)
{
for(j=0;j<outnode;j++)
{
fscanf_s(stream1, "%f", &wx1[i][j]); w1[i][j]=wx1[i][j];
}
}
fclose(stream1);
//隐含结点阀值读出
if((fopen_s(&stream2,"b1.txt","r"))==NULL)
cout<<"找到文件";
else
{
cout<<"打开文件失败!";
exit(1);
}
float xb1[hidenode];
for(i=0;i<hidenode;i++)
{
fscanf_s(stream2, "%f",&xb1[i]);
b1[i]=xb1[i];
}
fclose(stream2);
//输出结点阀值读出
if((fopen_s(&stream3,"b2.txt","r"))==NULL)
cout<<"找到文件";
else
{
cout<<"打开文件失败!";
exit(1);
}
float xb2[outnode];
for(i=0;i<outnode;i++)
{
fscanf_s(stream3, "%f",&xb2[i]);
b2[i]=xb2[i];
}
fclose(stream3);
}
void BpNet::test(double x)//网络构建完成后,测试数据
{
double x1[hidenode];//隐含结点状态值
double x2[outnode];//输出结点状态值
double o1[hidenode];//隐含层激活值
double o2[hidenode];//输出层激活值
for(int j=0;j<hidenode;j++)
{
o1[j]=0.0;
for(int i=0;i<innode;i++)
o1[j]=o1[j]+w[i][j]*x;//隐含层各单元输入激活值
x1[j]=1.0/(1.0+exp(-o1[j]-b1[j]));
}
for(int k=0;k<outnode;k++)
{
o2[k]=0.0;
for(int j=0;j<hidenode;j++)
o2[k]=o2[k]+w1[j][k]*x1[j]; //输出层各单元输入激活值
x2[k]=1.0/(1.0+exp(-o2[k]-b2[k]));
cout<<x2[k]<<endl;
}
}
//输入样本
double X[trainsample][innode]=
{ {0},{0.3925},{0.785},{1.18875},{1.57},{1.0467},{1.744},{2.093},{2.442},{2.791},{3.14} };
//期望输出样本
double Y[trainsample][outnode]=
{ {0},{0.3827},{0.7071},{0.9239},{1},{0.8660},{0.9848},{0.8660},{0.6427},{0.3420},{0} };
int main()
{
BpNet bp;
bp.init();
int times=0;
double testNum;
while(bp.error>0.0001)//0.0001)
{
bp.e=0.0;
times++;
bp.train(X,Y);
cout<<"Times="<<times<<" error="<<bp.error<<endl;
}
cout<<"trainning complete..."<<endl;
double m[innode]={1};
double *r=bp.recognize(m);
for(int i=0;i<outnode;++i)
cout<<bp.result[i]<<"";
double cha[trainsample][outnode];
double mi=100;
double index;
for(int i=0;i<trainsample;i++)
{
for(int j=0;j<outnode;j++)
{
//找差值最小的那个样本
cha[i][j]=(double)(fabs(Y[i][j]-bp.result[j]));
if(cha[i][j]<mi)
{
mi=cha[i][j];
index=i;
}
}
}
for(int i=0;i<innode;++i)
cout<<m[i];
cout<<" is "<<index<<endl;
cout<<endl;
cout<<"请输入测试值:"<<endl;
cin>>testNum;
bp.test(testNum);
cout<<"请输入测试值:"<<endl;
cin>>testNum;
bp.test(testNum);
cout<<"请输入测试值:"<<endl;
cin>>testNum;
bp.test(testNum);
getchar();
return 0;
}
总结:
在本次实验中,主要完成了神经网络的初级应用和货郎担问题的解决两大任务。

锻炼了
编程能力和调试能力,虽然说在后面的神经网络的编程中遇到了不少问题,最后的神经网络误差有点大,而且只能完成在x轴以上的正弦函数的模拟,但是已经有改善的方法。

可以从样本输入入手,从而完成全方面的拟合。

神经网络的拟合需要的时间和精确度还是有待调整,在今后的学习中在这几个方面需要加强。

相关文档
最新文档