人工智能搜索算法(深度優先、迭代加深、一致代價、A*搜索)

搜索算法問題求解java

1、需求分析算法

分別用深度優先、迭代加深、一致代價、A*搜索算法獲得從起始點Arad到目標點Bucharest的一條路徑,即爲羅馬尼亞問題的一個解,在求解的過程當中記錄每種算法獲得的解,即輸出每種解獲得的條路徑。數組

 

圖一:羅馬尼亞地圖 函數

2、詳細代碼性能

測試類:測試

/**Main類,打印各個算法的結果
* @author dyl
*
*/
classMain{
int result;
int xiabiao[]=null;//訪問的下標
publicstaticvoid main(String[] args){
Graph graph=newGraph();
System.out.println("----------------羅馬尼亞問題---------------");
System.out.println("一、深度優先搜索");
DFS dfs=new DFS();
dfs.DF_Search(graph,0,12);
System.out.println("二、迭代加深的搜索");
IDS ids=new IDS();
ids.IDS_Search(graph,0,12,15);//深度設15
System.out.println("三、一致代價搜索");
UCS ucs=new UCS(graph,0,12);
System.out.println("四、A*搜索");
AXing aXing=newAXing();
aXing.A_Search(graph, graph.H,0,15);//0-15即Arad到達Hirsova
}
/**打印
* @param g:圖
* @param stack:棧
*/
publicvoid show(Graph g,Stack stack){
if(stack.size()==0){
System.out.println("路徑搜索失敗");
return;
}
result=0;
System.out.print("訪問的下標: ");
for(int i =0; i < stack.size(); i++){
System.out.print("-->"+stack.get(i));
}
System.out.print("\n訪問過程: ");
xiabiao=newint[stack.size()];
if(stack.isEmpty()){
System.out.println("搜索失敗");
}else{
for(int i =0; i < stack.size(); i++){
System.out.print("-->"+g.cities[(Integer) stack.get(i)]);
}
for(int i =0; i < stack.size()-1; i++){
result+=g.path[(Integer) stack.get(i)][(Integer) stack.get(i+1)];
}
System.out.println("\n總長度爲:"+result+"\n");
g.markInit();//清空訪問
}
}
}
/**圖類
* @author dyl
*
*/
publicclassGraph{
publicint path[][]=newint[][]{{0,75,10000,118,140,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{75,0,71,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{10000,71,0,10000,151,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{118,10000,10000,0,10000,111,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{140,10000,151,10000,0,10000,80,99,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,111,10000,0,10000,10000,70,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,80,10000,0,10000,10000,10000,146,97,10000,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,99,10000,10000,0,10000,10000,10000,10000,211,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,10000,70,10000,10000,0,75,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,10000,10000,75,0,120,10000,10000,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,146,10000,10000,120,0,138,10000,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,97,10000,10000,10000,138,0,101,10000,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,10000,211,10000,10000,10000,101,0,90,85,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,90,0,10000,10000,10000,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,85,10000,0,98,10000,142,10000,10000},
{10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,98,0,86,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,86,0,10000,10000,10000},
{10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,142,10000,10000,0,92,10000},
{10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,92,0,87},
{10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,87,0}};
publicint[]H=newint[]{516,524,530,479,403,394,343,326,391,392,310,160,150,155,100,0};//啓發式函數
publicString[] cities=newString[]{"Arad","Zerind","Oradea","Timisoara","Sibiu","Lugoj",
"Rimnicu Vilcea","Fagaras","Mehadia","Drobeta","Craiova","Pitesti","Bucharest","Giurgiu","Urziceni","Hirsova",
"Eforie","Vaslui","Isi","Neamt"};//城市名
publicint[]mark=newint[20];//訪問標記
publicGraph(){//獲得數據
markInit();
}
 
/**
* 訪問標誌初始化
*/
publicvoid markInit(){
for(int i =0; i < mark.length; i++){
mark[i]=0;
}
}
/**第一個孩子
* @param g
* @param start
* @return -1表示一個孩子都沒有
*/
publicint getFirstVex(int start){
if(start>=0&&start<path.length){
for(int j =0; j < path.length; j++)
if(path[start][j]<10000&&path[start][j]>0)//有關係
return j;
}
return-1;
}
 
/**下一個孩子
* @param start
* @param w
* @return 表示圖G中頂點i的第j個鄰接頂點的下一個鄰接頂點
* 返回-1,表示後面沒有鄰接點了
*/
publicint getNextVex(int start,int w){
if(start>=0&&start<path.length&&w>=0&&w<path.length){
for(int i = w+1; i < path.length; i++)
if(path[start][i]<10000&&path[start][i]>0)
return i;
}
return-1;
}
publicint getNumber(){
return path.length;
}
}
 
l 【深度優先】

基本原理:深度優先搜索採用堆棧尋找路徑,首先從Arad結點出發,判斷是否爲目標結點,若否,尋找與該結點的鄰接點,先搜索一條分支上的全部節 點,而後再去搜索和Arad的其它分支結點,找出並存進待擴展結點表,等待擴展,每次先判斷待擴展結點表是否爲空,若否,則從待擴展結點表中取出一個結點 進行擴展,並將擴展後的結點存進該表,如果,則返回失敗。優化

深度優先搜索類:ui

/**深度優先搜索類
* @author dyl
*
*/
publicclass DFS {
Stack stack=newStack<Integer>();
int x;
int w;//v0的第一個鄰接點
/**深度優先搜索--非遞歸式
* @param g :圖
* @param v0:開始節點
* @param vg:最終節點
*/
publicvoid DF_Search(Graph g,int v0,int vg){
stack.push(v0);//入棧
g.mark[v0]=1;//v0被訪問
while(true){
x=(Integer) stack.peek();//查看棧頂元素
w=g.getFirstVex(x);
while(g.mark[w]==1){//被訪問,則尋找下一個鄰接點
w=g.getNextVex(x, w);
if(w==-1){
break;
}
}
while(w==-1){//沒有找到下一個鄰接點
stack.pop();
x=(Integer) stack.peek();
w=g.getFirstVex(x);
while(g.mark[w]==1){
w=g.getNextVex(x, w);
if(w==-1){
break;
}
}
}
stack.push(w);
g.mark[w]=1;
if(w==vg)break;//到達終點
}
newMain().show(g, stack);
}
}
實驗結果:

 

實驗分析:人工智能

根據結果可只知,在有限狀態空間下,樹搜索不是完備的,圖搜索完備;無限狀態下不完備。此結果0->1->2->4->6->10->11->12只是其中一條,但不是最優解。spa

分支因子b,深度d。則最壞狀況下時間複雜度也高達,空間複雜度  ,內存需求少。

 

l 【迭代加深】

基本原理:

迭代加深搜索是以DFS爲基礎的,它限制DFS遞歸的層數。

迭代加深搜索的基本步驟是:

一、設置一個固定的深度depth,一般是depth = 1,即只搜索初始狀態

二、DFS進行搜索,限制層數爲depth,若是找到答案,則結束,若是沒有找到答案 則繼續下一步

三、若是DFS途中遇到過更深的層,則++depth,並重復2;若是沒有遇到,說明搜 索已經結束,沒有答案

/**迭代加深
* @author dyl
*/
publicclass IDS {
Stack stack=newStack<Integer>();
/**迭代加深搜索
* @param g:圖
* @param v0:開始節點
* @param vg:目的節點
* @param depthMax:depthMax
*/
publicvoid IDS_Search(Graph g,int v0,int vg,int depthMax){
for(int i =2; i <=depthMax; i++){//迭代depthMax次
if(dfsearch(g, v0, vg,i)==1){
break;
}
}
}
/**深度搜索
* @param g:圖
* @param v0:開始節點
* @param vg:目的節點
* @param depthMax:depthMax
* @return
*/
publicint dfsearch(Graph g,int v0,int vg,int depthMax){
int x;
int w;//v0的第一個鄰接點
stack.push(v0);//入棧
g.mark[v0]=1;//v0被訪問
while(true){
x=(Integer) stack.peek();//查看棧頂元素
w=g.getFirstVex(x);
while(g.mark[w]==1){//被訪問,則尋找下一個鄰接點
w=g.getNextVex(x, w);
if(w==-1){
break;
}
}
while(w==-1){//沒有找到下一個鄰接點
stack.pop();
if(stack.size()==0){//清空了棧裏的元素
g.markInit();//訪問初始化
return0;
}
x=(Integer) stack.peek();
w=g.getFirstVex(x);
while(g.mark[w]==1){
w=g.getNextVex(x, w);
if(w==-1){
break;
}
}
}
stack.push(w);
g.mark[w]=1;
if(w==vg)
{
break;
}//檢查是否達到終點
if(stack.size()>=depthMax){//從新迭代則從新初始化值
stack.pop();
}
}
newMain().show(g, stack);
return1;
}
}

實驗結果:

 

實驗分析:

         由於迭代加深是從按照深度的遞增搜索的,因此說0-》1-》2-》4-》7-》12這條 路徑,只是在深度最低的狀況下找到的結果,並非最優解。是完備的,時間複雜度也 高達,空間複雜度。

 

l 【一致代價】

基本原理:

擴展的是路徑消耗g(n)最小的節點n,用優先隊列來實現,對解的路徑步數不關心,只關心路徑總代價。即便找到目標節點也不會結束,而是再檢查新路徑是否是要比老路徑好,確實好,則丟棄老路徑。

 
/**
* 一致代價類
*
*/
publicclass UCS {
 
public UCS(Graph g,int start,int end){
int[] pre =newint[20];// 保存各個結點的前驅結點
int[] dist =newint[20];// 用於保存當前結點到起始結點的實際路徑長度
for(int i =0; i < pre.length; i++)
{
pre[i]=-1;
dist[i]=10000;
}
// 調用一致搜索算法搜索路徑
UC_search(g,start, end, dist, pre);
// 打印路徑顯示函數
displayPath(start, end, pre,g);
}
/**
* @param start:開始
* @param goal:目的
* @param prev:前驅節點
* @param g:圖
*/
publicvoid displayPath(int start,int goal,int[] prev,Graph g)
{
Stack<Integer> stack =newStack<Integer>();
stack.push(goal);
while(prev[goal]!= start)
{
stack.push(prev[goal]);
goal = prev[goal];
}
stack.push(start);
System.out.print("訪問的下標: ");
for(int i = stack.size()-1; i >=0; i--){
System.out.print("-->"+stack.get(i));
}
System.out.print("\n訪問過程: ");
for(int i = stack.size()-1; i >=0; i--){
System.out.print("-->"+ g.cities[stack.get(i)]);
}
System.out.print("\n總長度爲: ");
int result=0;
for(int i =0; i < stack.size()-1; i++){
result+=g.path[stack.get(i)][stack.get(i+1)];
}
System.out.print(result);
System.out.println("\n");
g.markInit();
}
/**
* @param g:圖
* @param start:開始
* @param goal:目的
* @param prev:前驅節點
*
*/
publicvoid UC_search(Graph g,int start,int goal,int[] dist,int[] pre)
{
List<Integer> list =newArrayList<Integer>();
list.add(start);
while(!list.isEmpty())
{
moveMinToTop(list, dist);// 將dist數組中最小值所對應的結點,移至list隊首
int current = list.remove(0);// 將list隊首的結點出隊,並展開
g.mark[current]=1;
if(current == goal)
{
return;
}
 
for(int j =0; j < g.path[current].length; j++)
{
if(g.path[current][j]<10000&& g.mark[j]==0)
{
if(!isInList(j, list))// 結點j不在隊列裏
{
list.add(j);
pre[j]= current;
dist[j]= dist[current]+ g.path[current][j];
}
elseif((dist[current]+ g.path[current][j])< dist[j])
{
pre[j]= current;
dist[j]= dist[current]+ g.path[current][j];
}
}
}
if(list.isEmpty())
{
System.out.println("搜索不成功!");
}
}
}
/**
* 檢查結點a,是否在隊列list裏
*/
publicboolean isInList(int a,List<Integer> list)
{
for(int i =0; i < list.size(); i++)
{
if(list.get(i)== a)
{
returntrue;
}
}
returnfalse;
}
/**
* 將dist數組中的最小值所對應的結點,從list隊列中移至隊列頭
*/
publicvoid moveMinToTop(List<Integer> list,int[] dist)
{
int index =0;
int min = dist[index];
for(int i =0; i < list.size(); i++)
{
int a = list.get(i);
if(dist[a]< min)
{
index = i;
min = dist[a];
}
}
int temp = list.get(index);
for(int i = index; i >0; i--)
{
list.set(i, list.get(i -1));
}
list.set(0, temp);
}
}
實驗結果:

 

實驗分析:

從結果0-》4-》6-》11-》12能夠看出。是最優解,他的複雜度不能簡單地使用b、d刻畫。得使用C*表示最優解的耗散值。時間複雜度,空間複雜度。

 

l 【A*搜索】

基本原理:

公式表示爲: f(n)=g(n)+h(n),

其中 f(n) 是從初始點經由節點n到目標點的估價函數,

g(n) 是在狀態空間中從初始節點到n節點的實際代價,

h(n) 是從n到目標節點最佳路徑的估計代價。

保證找到最短路徑(最優解的)條件,關鍵在於估價函數f(n)的選取:

首先將起始結點S放入OPEN表,CLOSE表置空,算法開始時:     

一、若是OPEN表不爲空,從表頭取一個結點n,若是爲空算法失敗。     

二、n是目標解嗎?是,找到一個解(繼續尋找,或終止算法)。     

三、將n的全部後繼結點展開,就是從n能夠直接關聯的結點(子結點),若是不在CLOSE表中,就將它們放入OPEN表,並把S放入CLOSE表,同時計算每個後繼結點的估價值f(n),將OPEN表按f(x)排序,最小的放在表頭,重複算法,回到1。

import java.util.Stack;
 
/** A*搜索類
* @author dyl
*
*/
publicclassAXing{
intMaxWeight=10000;//表示無窮大
Stack stack=newStack<Integer>();
/**A*搜索
* @param g:圖
* @param H:啓發式函數值
* @param v0:初始值
* @param end:目標值
*/
publicvoid A_Search(Graph g,int H[],int v0,int end){
boolean flag=true;
int x;//表示棧頂元素
int vex;//尋找目標節點
intMinF,MinVex= v0;//記錄最小的f(n)和對應的節點
int[][]GHF=newint[g.path.length][3];//分別用於存儲g(n),h(n),f(n)
for(int i =0; i < g.path.length; i++){
GHF[i][0]=0;
GHF[i][2]=MaxWeight;//對f(n)初始化,1000表示無窮大
}
stack.push(v0);//v0入棧
GHF[v0][0]=0;//g(n)
GHF[v0][1]=H[v0];//h(n)
GHF[v0][2]=GHF[v0][0]+GHF[v0][1];//f(n)
g.mark[v0]=1;
while(flag){
MinF=MaxWeight;
x=(Integer) stack.peek();
//處理第一個子節點
vex=g.getFirstVex(x);
if(vex==end){//找到目標節點
stack.push(vex);
g.mark[vex]=1;
break;
}
if(vex!=-1){//子節點能找到,繼續
if(g.mark[vex]==0){//沒被訪問
GHF[vex][0]=GHF[x][0]+g.path[x][vex];//節點vex的g(n)
GHF[vex][1]=H[vex];//節點vex的h(n)
GHF[vex][2]=GHF[vex][0]+GHF[vex][1];
if(GHF[vex][2]<MinF){
MinF=GHF[vex][2];
MinVex=vex;
}
}
//處理剩下的鄰接點(寬度遍歷)
while(vex!=-1){
vex=g.getNextVex(x, vex);
if(vex!=-1&&g.mark[vex]==0){//有鄰節點
GHF[vex][0]=GHF[x][0]+g.path[x][vex];//節點vex的g(n)
GHF[vex][1]=H[vex];//節點vex的h(n)
GHF[vex][2]=GHF[vex][0]+GHF[vex][1];
if(GHF[vex][2]<MinF){
MinF=GHF[vex][2];
MinVex=vex;
}
}
if(vex==-1){//沒有鄰接點了,此時肯定最小消耗節點,並壓棧
stack.push(MinVex);
g.mark[MinVex]=1;
break;
}
if(vex==end){
stack.push(vex);//壓棧目標節點
g.mark[vex]=1;
flag=false;
break;
}
}
}
else{//沒有子節點或者子節點被訪問了,循環出棧
while(vex==-1){
stack.pop();
}
}
}
newMain().show(g, stack);
}
}

實驗結果:

 

 實驗分析:

A*搜索估價值h(n)<= n到目標節點的距離實際值,這種狀況下,搜索的點數多,搜索範圍大,效率低。但能獲得最優解。而且若是h(n)=d(n),即距離估計h(n)等於最短距離,那麼搜索將嚴格沿着最短路徑進行, 此時的搜索效率是最高的。若是 估價值>實際值,搜索的點數少,搜索範圍小,效率高,但不能保證獲得最優解。

 

3、實驗結果:

 

4、思考題

一、根據實驗結果分析深度優先搜索,一致代價搜索,迭代加深的深度優先搜索算法的時間和空間複雜度。


二、根據實驗結果分析A*搜索的性能。 

答:A*算法是一種靜態路網中求解最短路徑最有效的直接搜索方法。估價值與實際值越接近,估價函數取得就越好。0-》4-》6-》11-》12-14-》15從圖中能夠看出是最優解,估價值h(n)<= n到目標節點的距離實際值,這種狀況下,搜索的點數多,搜索範圍大,效率低。但能獲得最優解。而且若是h(n)=d(n),即距離估計h(n)等於最短距離,那麼搜索將嚴格沿着最短路徑進行, 此時的搜索效率是最高的。若是 估價值>實際值,搜索的點數少,搜索範圍小,效率高,但不能保證獲得最優解。

   

該實驗是人工智能的實驗,以前作的時候沒考慮到代碼優化,因此代碼量有點大,請你們見諒。若有不對的地方,但願你們提出建議。

相關文章
相關標籤/搜索