日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

题目2:隐式图的搜索问题(A*算法解决八数码)

發(fā)布時間:2025/3/21 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 题目2:隐式图的搜索问题(A*算法解决八数码) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據結構課程實踐系列

題目1:學生成績檔案管理系統(tǒng)(實驗準備)

題目2:隱式圖的搜索問題(A*算法解決八數碼)

題目3:文本文件單詞的檢索與計數(實驗準備)

文章目錄

  • 數據結構課程實踐系列
    • 題目1:學生成績檔案管理系統(tǒng)(實驗準備)
    • 題目2:隱式圖的搜索問題(A*算法解決八數碼)
    • 題目3:文本文件單詞的檢索與計數(實驗準備)
  • 聲明
    • 題目要求
    • 何為八數碼?
    • 狀態(tài)如何表示
    • 所需知識
  • 導出所需知識
    • 優(yōu)先隊列BFS算法缺陷
    • A*搜索算法
    • 總結:
  • 實際操作
    • 搜索過程描述
    • 啟發(fā)式策略工作工程:紅圈數字表示擴展順序
  • 代碼實現
    • Map+BFS+A*
    • Hash+BFS+A*
  • GAMEOVER

聲明

題目要求

看過這次實驗要求之后

總結:利用A*來解決八數碼問題,狀態(tài)很好找,每次移動空格就會形成一種新的狀態(tài),

何為八數碼?

八數碼游戲包括一個3X3的棋盤,棋盤上擺放著8個數字的棋子,留下一個空位。與空位相鄰的棋子可以滑動到空位中。游戲的目的是要達到一個特定的目標狀態(tài)。標注的形式化如下(舉例):

23
184
765

(初始狀態(tài))

123
84
765

(目標狀態(tài))

狀態(tài)如何表示

  • 每個狀態(tài)都用3*3的數組表示,但是BFS中需要入隊出隊,比較麻煩而且空間占用較大
  • 狀態(tài)壓縮,采用一個整數保存狀態(tài)的數字序列,例如狀態(tài)1表示為203184765,狀態(tài)2表示為123804765
  • 所需知識

    優(yōu)先隊列BFS算法,因為A*算法就是帶有估價函數的優(yōu)先隊列BFS

    導出所需知識

    優(yōu)先隊列BFS算法缺陷

    該算法維護了一個優(yōu)先隊列(二叉堆),不斷從堆中取出“當前代價最小”的狀態(tài)(堆頂)去進行擴展。但是它得到只是初態(tài)到該狀態(tài)的最小代價并沒有去考慮從該狀態(tài)出發(fā)到目標狀態(tài)的情況)

    舉例說明:
    如果給定一個“目標狀態(tài)”,需要求出從初態(tài)到目標狀態(tài)的最小代價,優(yōu)先隊列BFS顯然不行。因為一個狀態(tài)的當前代價最小,只能說明從起始狀態(tài)到該狀態(tài)的代價很小,而在未來的探索中,從該狀態(tài)到目標狀態(tài)可能會花費很大的代價另外一些狀態(tài)雖然當前代價略大,但是未來到目標狀態(tài)的代價可能會很小,于是從起始狀態(tài)到目標狀態(tài)的總代價反而更優(yōu)。

    A*搜索算法

    于是,我們?yōu)榱私鉀Q上面的問題:可以對未來可能產生的代碼進行預估。詳細地講,我們去設計一個**“估價函數”**,以任意狀態(tài)為輸入,計算出從該狀態(tài)到目標狀態(tài)所需代價的估計值。在搜索之中,仍然維護了一個堆,不斷從堆中取出“當前代價+未來估價”最小的狀態(tài)來進行擴展。

    為了保證第一次從堆中取出目標狀態(tài)時得到的就是最優(yōu)解,我們設計的估價函數需要滿足一個基本準則:

    設當前狀態(tài)state到目標狀態(tài)所需代價的估計值為f(state);設在未來的探索中,實際求出的從當前狀態(tài)state到目標狀態(tài)的最小代價為g(state)
    對于任意的state,應該有f(state)<=g(state)

    也就是說,估價函數的估值不能大于未來實際代價,估價比實際代價更優(yōu)。估價函數f(n)=g(n)+h(n)

    總結:

    這種帶有估價函數的優(yōu)先隊列BFS就成為A* 算法。只要保證對于任意狀態(tài)state,都有f(state)<=g(state),A* 算法就一定能在目標狀態(tài)第一次從堆中被取出時得到最優(yōu)解,并且在搜索過程中每個狀態(tài)只需要擴展一次(之后再被取出就可以直接忽略 )。估價f(state)越準確,越接近g(state),A*算法的效率越高。如果估價始終為0,就等于普通的優(yōu)先隊列BFS。

    實際操作

    搜索過程描述

    A算法又稱為啟發(fā)式搜索算法。對啟發(fā)式搜索算法,又可根據搜索過程中選擇擴展節(jié)點的范圍,將其分為全局擇優(yōu)搜索算法局部擇優(yōu)搜索算法

    在全局擇優(yōu)搜索中,每當需要擴展節(jié)點時,總是從 Open 表的所有節(jié)點中選擇一個估價函數值最小的節(jié)點進行擴展。其搜索過程可能描述如下:

  • 把初始節(jié)點 S0 放入 Open 表中, f(S0)=g(S0)+h(S0)
  • 如果 Open 表為空,則問題無解,失敗退出;
  • 把 Open 表的第一個節(jié)點取出放入 Closed 表,并記該節(jié)點為 n ;
  • 考察節(jié)點 n 是否為目標節(jié)點。若是,則找到了問題的解,成功退出;
  • 若節(jié)點 n 不可擴展,則轉到第 (2) 步;
  • 擴展節(jié)點 n ,生成子節(jié)點 ni ( i =1,2, …… ) ,計算每一個子節(jié)點的估價值 f( ni ) ( i =1,2, …… ) ,并為每一個子節(jié)點設置指向父節(jié)點的指針,然后將這些子節(jié)點放入 Open 表中;
  • 根據各節(jié)點的估價函數值,對 Open 表中的全部節(jié)點按從小到大的順序重新進行排序;
  • 轉第 (2) 步。
  • 這里采用的啟發(fā)式策略為:f(n) = g(n) + h(n),其中g(n)為從初始節(jié)點到當前節(jié)點的步數(層數),h(n)為 當前節(jié)點 “不在位 ”的方塊數(也就是說不在位的方塊數越少,那么臨目標狀態(tài)越近)例如下圖中的h(n)=5,有的講解的是不包含空格,我這里是包含了的,經測試只要前后標準一致,包不包含空格都一樣。
    g(n)為已經消耗的實際代價,即已經走了的步數
    h(n)為預測路徑,即還有幾個數字待走


    h(n)=5

    啟發(fā)式策略工作工程:紅圈數字表示擴展順序

    代碼實現

    Map+BFS+A*

    #include<cstdio> #include<queue> #include<map> using namespace std; char arr[10], brr[10] = "123804765"; struct node {int num, step, cost, zeroPos;bool operator<(const node& a)const {return cost > a.cost;}node(int n, int s, int p) {num = n, step = s, zeroPos = p;setCost();}void setCost() {char a[10];int c = 0;sprintf(a, "%09d", num);for (int i = 0; i < 9; i++)if (a[i] != brr[i])c++;cost = c + step;} }; int des = 123804765; int changeId[9][4] = { {-1,-1,3,1},{-1,0,4,2},{-1,1,5,-1},{0,-1,6,4},{1,3,7,5},{2,4,8,-1},{3,-1,-1,7},{4,6,-1,8},{5,7,-1,-1} }; map<int, bool>mymap; priority_queue<node> que;//優(yōu)先級隊列 void swap(char* ch, int a, int b) { char c = ch[a]; ch[a] = ch[b]; ch[b] = c; } int bfsHash(int start, int zeroPos) {char temp[10];node tempN(start, 0, zeroPos);//創(chuàng)建一個節(jié)點 que.push(tempN);//壓入優(yōu)先級隊列 mymap[start] = 1;//標記開始節(jié)點被訪問過 while (!que.empty()) {tempN = que.top();que.pop();//彈出一個節(jié)點 sprintf(temp, "%09d", tempN.num);int pos = tempN.zeroPos, k;for (int i = 0; i < 4; i++) {if (changeId[pos][i] != -1) {swap(temp, pos, changeId[pos][i]);sscanf(temp, "%d", &k);if (k == des)return tempN.step + 1;if (mymap.count(k) == 0) {node tempM(k, tempN.step + 1, changeId[pos][i]);que.push(tempM);//創(chuàng)建一個新節(jié)點并壓入隊列 mymap[k] = 1;}swap(temp, pos, changeId[pos][i]);}}} } int main() {int n, k, b;scanf("%s", arr);for (k = 0; k < 9; k++)if (arr[k] == '0')break;sscanf(arr, "%d", &n);b = bfsHash(n, k);printf("%d步即可變換完成", b);return 0; }


    所得結果跟我們上圖分析結果一樣,

    changeId[9][4] = { {-1,-1,3,1},{-1,0,4,2},{-1,1,5,-1},{0,-1,6,4},{1,3,7,5},{2,4,8,-1},{3,-1,-1,7},{4,6,-1,8},{5,7,-1,-1} };

    這個數組也就代表九個位置中四個方向的可置換的數組元素下標(-1表示該方向不能交換),注意:這里的四個方向的話,是逆時針,(上左下右)

    Hash+BFS+A*

    #include<cstdio> #include<queue> using namespace std; char arr[10],brr[10]="123804765"; struct node{int num,step,cost,zeroPos;bool operator<(const node &a)const{return cost>a.cost;}node(int n,int s,int p){num=n,step=s,zeroPos=p;setCost();}void setCost(){char a[10];int c=0;sprintf(a,"%09d",num);for(int i=0;i<9;i++)if(a[i]!=brr[i])c++;cost=c+step;} }; int changeId[9][4]={{-1,-1,3,1},{-1,0,4,2},{-1,1,5,-1},{0,-1,6,4},{1,3,7,5},{2,4,8,-1},{3,-1,-1,7},{4,6,-1,8},{5,7,-1,-1}}; const int M=2E+6,N=1000003;//362897; int hashTable[M];//hashtable中key為hash值,value為被hash的值 int Next[M],des=123804765;//next表示如果在某個位置沖突,則沖突位置存到hashtable[next[i]] priority_queue<node> que;//優(yōu)先級隊列 int Hash(int n){return n%N; } bool tryInsert(int n){int hashValue=Hash(n);while(Next[hashValue]){//如果被hash出來的值得next不為0則向下查找 if(hashTable[hashValue]==n)//如果發(fā)現已經在hashtable中則返回false return false; hashValue=Next[hashValue];}//循環(huán)結束hashValue指向最后一個hash值相同的節(jié)點 if(hashTable[hashValue]==n)//再判斷一遍 return false; int j=N-1;//在N后面找空余空間,避免占用其他hash值得空間造成沖突 while(hashTable[++j]);//向后找一個沒用到的空間 Next[hashValue]=j;hashTable[j]=n;return true; } void swap(char* ch,int a,int b){char c=ch[a];ch[a]=ch[b];ch[b]=c;}int bfsHash(int start,int zeroPos){char temp[10];node tempN(start,0,zeroPos); que.push(tempN);while(!que.empty()){tempN=que.top();que.pop();sprintf(temp,"%09d",tempN.num);int pos=tempN.zeroPos,k;for(int i=0;i<4;i++){if(changeId[pos][i]!=-1){swap(temp,pos,changeId[pos][i]);sscanf(temp,"%d",&k);if(k==des)return tempN.step+1;if(tryInsert(k)){//插入新狀態(tài)成功,則說明新狀態(tài)沒有被訪問過 node tempM(k,tempN.step+1,changeId[pos][i]);que.push(tempM);}swap(temp,pos,changeId[pos][i]);}}} } int main(){int n,k,b=0;scanf("%s",arr);for(k=0;k<9;k++)if(arr[k]=='0')break;sscanf(arr,"%d",&n);if(n!=des)b=bfsHash(n,k);printf("%d步即可變換完成",b); return 0; }

    GAMEOVER

    總結

    以上是生活随笔為你收集整理的题目2:隐式图的搜索问题(A*算法解决八数码)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。