八数码问题是一个经典的搜索问题,本文将介绍如何使用启发式搜索—— AStar 算法来求解八数码问题。
问题描述算法描述 预估值的设计八数码问题的A星搜索算法实现
要求:设计估价函数,并采用c或python编程实现,以八数码为例演示A星算法的搜索过程,争取做到直观、清晰地演示算法,代码要适当加注释。
八数码难题:在3×3方格棋盘上,分别放置了标有数字1,2,3,4,5,6,7,8的八张牌,初始状态S0可自己随机设定,使用的操作有:空格上移,空格左移,空格右移,空格下移。试采用A*算法编一程序实现这一搜索过程。
A* 算法的花费为 f(n) = g(n) + h(n),其中 g(n) 为搜索深度,定义为状态单元 state 的成员变量,在每次生成子节点时将其加一。h(n) 为不对位的将牌数,将该部分的计算重载于 state 的小于运算符中,并将 f(n) = g(n) + h(n) 的值作为状态单元的比较值。
数据结构设计- 每个状态用一个结构体表示,其中 depth 为状态深度,str 为该状态字符串,并重载小于运算符用于计算最优。
- open 表使用优先队列 priority_queue,实现在 O(logn) 的时间复杂度内获取最优值。
- close 表使用哈希集合 unordered_set,实现在 O(1) 时间复杂度内判断某状态是否已位于 close 表中。
- 而为了得到最优搜索路径,还需要将每个状态的前驱加以保存,前驱表 pre 我使用了哈希表 unordered_map,模板类型为 pair
,表示 key 的前驱为 value。
#include运行结果 输入#include #include #include #include #include #include using namespace std; class Solution { private: static string targetStr; const vector > dirs = { {-1,0},{1,0},{0,-1},{0,1} }; // 四个移动方向 struct state { string str; int depth; state(string s, int d) : str(s), depth(d) {} bool operator < (const state& s) const { int cnt1 = 0, cnt2 = 0; for (int i = 0; i < 9; ++i) { if (s.str[i] != targetStr[i]) ++cnt1; if (this->str[i] != targetStr[i]) ++cnt2; } return cnt1 + this->depth < cnt2 + s.depth; // f(n) = g(n) + h(n) } }; inline void swapChr(string& child, int iniInd, int childInd) { // 交换字符,完成移动 child[iniInd] = child[childInd]; child[childInd] = '0'; } void printPath(unordered_map & pre, string path) { // 输出路径 stack st; while (path != "None") { st.emplace(path); path = pre[path]; } int cnt = 0; while (++cnt && !st.empty()) { string str = st.top(); st.pop(); cout << "step" << cnt << ": " << str.substr(0, 3) << endl << " " << str.substr(3, 3) << endl << " " << str.substr(6, 3) << endl << string(15, '-') << endl; } } public: void eightDigitalQues(const string& ini, const string& target) { targetStr = target; priority_queue open; unordered_set close; unordered_map pre; open.emplace(ini, 0); pre[ini] = "None"; while (!open.empty()) { string n = open.top().str; int d = open.top().depth; open.pop(); close.emplace(n); if (n == target) { break; } int iniInd = n.find('0'); int x = iniInd / 3, y = iniInd % 3; for (const auto& dir : dirs) { // 尝试选择四个方向 int nx = x + dir[0], ny = y + dir[1]; if (nx >= 0 && nx <= 2 && ny >= 0 && ny <= 2) { // 满足移动后下标满足条件 int childInd = nx * 3 + ny; state childState(n, d + 1); swapChr(childState.str, iniInd, childInd); if (close.count(childState.str)) // 如该状态已加入close表,则跳过 continue; open.emplace(childState); // 加入满足条件的子状态 pre[childState.str] = n; // 更新前驱 } } } printPath(pre, target); // 输出流程 return; } }; string Solution::targetStr; int main() { Solution S; string ini, target; cin >> ini >> target; S.eightDigitalQues(ini, target); cin.get(); }
原状态:283164705, 目标状态:123804765
输出


