启发式搜索
啟發式搜索
啟發式搜索就是在狀態空間中的搜索對每一個搜索的位置進行評估,得到最好的位置,再從這個位置進行搜索直到目標。這樣可以省略大量無謂的搜索路徑,提高了效率。在啟發式搜索中,對位置的估價是十分重要的。采用了不同的估價可以有不同的效果。
在啟發式搜索中,我們每次找到當前“最有希望是最短路徑”的狀態進行擴展。對于每個狀態的我們用函數F來估計它是否有希望。F包含兩個部分:
F = G + HG:就是普通寬度優先搜索中的從起始狀態到當前狀態的代價,
H:是一個估計的值,表示從當前狀態到目標狀態估計的代價。
H是由我們自己設計的,H函數設計的好壞決定了啟發式算法的效率。H值越大,算法運行越快。
但是在設計評估函數時,需要注意一個很重要的性質:評估函數的值一定要小于等于實際當前狀態到目標狀態的代價。
否則雖然你的程序運行速度加快,但是可能在搜索過程中漏掉了最優解。相對的,只要評估函數的值小于等于實際當前狀態到目標狀態的代價,就一定能找到最優解
F:評估值和狀態值的總和。
?
同時在啟發式搜索中將原來的一個隊列變成了兩個隊列:openlist和closelist。
在openlist中的狀態,其F值還可能發生變化。而在closelist中的狀態,其F值一定不會再發生改變。
整個搜索解的流程變為:
利用這個方法可以避免搜索一些明顯會遠離目標狀態的狀態,從而縮小搜索空間,早一步搜索到目標結果。
在啟發式搜索中,最重要的是評估函數的選取,一個好的評估函數能夠更快的趨近于目標狀態。
?
啟發式搜索在某些情況下并不一定好用,一方面取決于評估函數的選取,另一個方面由于在選取狀態時也會有額外的開銷。而快速趨近目標結果所減少的時間,能否彌補這一部分開銷也是非常關鍵的。
所以根據題目選取合適的搜索方法才是最重要的。
?
問題:八數碼求解的步數
偽代碼:
search(status):start.status = statusstart.g = 0 // 實際步數start.h = evaluate(start.status)start.f = start.g + start.hopenlist.insert(start)While (!openlist.isEmpty()) u = openlist.getMinFStatus()closelist.insert(u)For v is u.neighborStatusIf (v in openlist) Then// 更新v的f值If (v.f > v.h + u.g + 1) Thenv.f = v.h + u.g + 1End IfElse If (v in closelist)continueElse v.g = u.g + 1v.h = evaluate(v.status)v.f = v.g + v.hopenlist.insert(v)End IfEnd ForEnd While源碼:
#include <cstring> #include <cmath> #include <queue> #include <stack> #include <algorithm> #include <iostream> using namespace std; struct node{ int f,h,g; int x,y;char map[3][3]; friend bool operator< (const node &a,const node &b){if(a.f==b.f) return a.g<b.g; return a.f>b.f; } }; node start; int Hash[15]; // Hash[i] 存 i! 1~9 int pos[][2]= {{0,0},{0,1},{0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}}; // 0~8 的目標位置的坐標 int to[4][2]={0,-1,0,1,-1,0,1,0}; bool vis[500000]; //判斷不可能的狀況 bool check(){ int s[20]; int cnt = 0; for(int i = 0; i<3; i++){ for(int j = 0; j<3; j++){ s[3*i+j] = start.map[i][j]; if(s[3*i+j] == 'x') continue; for(int k = 3*i+j-1; k>=0; k--){if(s[k] == 'x') continue; if(s[k]>s[3*i+j]) cnt++; } } } if(cnt%2) return false; return true; } //康托 int solve(node a){ int s[20]; int ans = 0; for(int i = 0; i<3; i++){ for(int j = 0; j<3; j++){ s[3*i+j] = a.map[i][j]; int cnt = 0; for(int k = 3*i+j-1; k>=0; k--){ if(s[k]>s[3*i+j]) cnt++; } ans = ans+Hash[i*3+j]*cnt; } } return ans; } //得到 H值 ,曼哈頓距離之和 int get_h(node a){ int ans = 0; for(int i = 0; i<3; i++){ for(int j = 0; j<3; j++){ if(a.map[i][j] == 'x') continue; int k = a.map[i][j]-'1'; ans+=abs(pos[k][0]-i)+abs(pos[k][1]-j); }} return ans; }int bfs(){ memset(vis,0,sizeof(vis)); // queue<node> Q; priority_queue<node> Q; start.g = 0; start.h = get_h(start); start.f = start.h; vis[solve(start)]=true;if(solve(start)==0) return 0;Q.push(start); node next;while(!Q.empty()){ node a = Q.top(); Q.pop(); // node a = Q.front();int k_s = solve(a); vis[k_s]=true;for(int i = 0; i<4; i++){next = a; next.x+=to[i][0]; next.y+=to[i][1]; if(next.x < 0 || next.y < 0 || next.x>2 || next.y > 2) continue; next.map[a.x][a.y] = a.map[next.x][next.y]; next.map[next.x][next.y] = 'x'; next.g+=1; next.h = get_h(next); next.f = next.g+next.h; int k_n = solve(next); if(vis[k_n]) continue; Q.push(next); if(k_n == 0) return next.g; } } } int main(){ Hash[0] = 1; for(int i = 1; i<=9; i++) Hash[i] = Hash[i-1]*i; int t=0;cin>>t;char a=0;while(t--){for (int i=0;i<3;i++){for (int j=0;j<3;j++){cin>>a;start.map[i][j]=a;if(a=='0'){start.map[i][j]='x';start.x=i;start.y=j;}}}if(!check()){ cout<<"No Solution!"<<endl; } else cout<<bfs()<<endl; } return 0; } View Code?
轉載于:https://www.cnblogs.com/ygdblogs/p/5578653.html
總結
- 上一篇: 离线安装ADT和sdk
- 下一篇: 在全局中调用类的静态成员函数