A*算法 -- 八数码问题和传教士过河问题的代码实现

时间:2022-09-22 10:46:37

  前段时间人工智能的课介绍到A*算法,于是便去了解了一下,然后试着用这个算法去解决经典的八数码问题,一开始写用了挺久时间的,后来试着把算法的框架抽离出来,编写成一个通用的算法模板,这样子如果以后需要用到A*算法的话就可以利用这个模板进行快速开发了(对于刷OJ的题当然不适合,不过可以适用于平时写一些小游戏之类的东西)。

  A*算法的原理就不过多介绍了,网上能找到一大堆,核心就是估价函数 g() 的定义,这个会直接影响搜索的速度,我在代码里使用 C++/Java 的多态性来编写业务无关的算法模板,用一个抽象类来表示搜索树中的状态,A*算法主类直接操纵这个抽象类,然后编写自己业务相关的类去继承这个抽象类并实现其中的所有抽象方法(C++里是纯虚函数),之后调用A*算法主类的 run 函数就能得到一条可行并且是最短的的搜索路径,下面具体看代码:(文末附所有代码的 github 地址)

先看 c++ 部分,毕竟一开始就是用 c++ 来写的

首先是表示状态的抽象基类CState,头文件 state.h:

#ifndef  __state_h
#define __state_h #include <cstddef>
#include <vector>
using std::vector; class CState
{
public:
CState();
virtual bool operator < (const CState &) const=;
virtual void checkSomeFields(const CState &) const;
virtual vector<CState*> getNextState() const=;
vector<CState*> __getNextState() const; // call the function getNextState and deal with iSteps and pparent
virtual long astar_f() const;
virtual long astar_g() const=; // g函数的值越小,优先级就越高,f()和h()函数类似
virtual long astar_h() const;
virtual ~CState(); int iSteps;
const CState *pparent; // 必须指向实实际际存在的值!注意不要指向一个局部变量等!
}; #endif

源文件 state.cpp:

#include "state.h"
#include <algorithm>
using std::for_each; CState::CState(): iSteps(), pparent(NULL) {} void CState::checkSomeFields(const CState &) const {} vector<CState*> CState::__getNextState() const
{
vector<CState*> nextState = getNextState();
for_each(nextState.begin(), nextState.end(), [this](CState *pstate) {
pstate->iSteps = this->iSteps + ;
pstate->pparent = this;
});
return nextState;
} long CState::astar_f() const
{
return iSteps;
} long CState::astar_h() const
{
return astar_f() + astar_g();
} CState::~CState() {}

  子类只需实现小于运算符,getNextState(),astar_g() 这三个纯虚函数就可以了,另外几个虚函数可以不重写,直接用父类的即可。

  然后是A*算法主类 CAstar,头文件 astar.h:

#ifndef  __ASTAR_H
#define __ASTAR_H #include "state.h"
#include <set>
using std::set; class CAstar
{
public:
CAstar(const CState &_start, const CState &_end);
static set<const CState*> getStateByStartAndSteps(const CState &start, int steps);
void run();
~CAstar(); const CState &m_rStart, &m_rEnd;
bool bCanSolve;
int iSteps;
vector<const CState*> vecSolve;
long lRunTime;
int iTotalStates;
private:
set<const CState*> pointerWaitToDelete;
}; #endif

源文件 astar.cpp:

#include "astar.h"
#include "timeval.h"
#include "exception.h"
#include <set>
#include <queue>
#include <algorithm>
#include <cstdlib>
#include <functional>
using std::set;
using std::queue;
using std::priority_queue;
using std::swap;
using std::max;
using std::sort;
using std::function;
#define For(i,s,t) for(auto i = (s); i != (t); ++i) CAstar::CAstar(const CState &_start, const CState &_end):
m_rStart(_start), m_rEnd(_end), bCanSolve(false), iSteps(), vecSolve{},
iTotalStates(), lRunTime(), pointerWaitToDelete{}
{
m_rStart.checkSomeFields(m_rEnd);
} template <typename T>
struct CPointerComp
{
bool operator () (const T &pl, const T &pr) const
{
return *pl < *pr;
}
}; set<const CState*> CAstar::getStateByStartAndSteps(const CState &start, int steps)
{
set<const CState*> retSet;
set<const CState*, CPointerComp<const CState*> > inSet;
inSet.insert(&start);
queue<const CState*> queState;
queState.push(&start);
while(!queState.empty()) {
const CState* const pCurState = queState.front();
queState.pop();
if(pCurState->iSteps > steps) {
continue;
}
if(pCurState->iSteps == steps) {
retSet.insert(pCurState);
continue;
}
auto nextState = pCurState->__getNextState();
int len = nextState.size();
For(i, , len) {
if(inSet.find(nextState[i]) == inSet.end()) {
queState.push(nextState[i]);
inSet.insert(nextState[i]);
} else {
delete nextState[i];
}
}
}
inSet.erase(&start);
For(ret_it, retSet.begin(), retSet.end()) {
inSet.erase(*ret_it);
}
For(ins_it, inSet.begin(), inSet.end()) {
delete *ins_it;
}
return retSet;
} struct priority_state
{
bool operator () (const CState* const lhs, const CState* const rhs) const
{
return lhs->astar_h() > rhs->astar_h();
}
}; void CAstar::run()
{
CTimeVal _time; set<const CState*, CPointerComp<const CState*>> setState;
setState.insert(&m_rStart);
priority_queue<const CState*, vector<const CState*>, priority_state> queState;
queState.push(&m_rStart); while(!queState.empty()) {
// auto pHeadState = *(setState.find(queState.top()));
auto pHeadState = queState.top();
queState.pop();
if(!(*pHeadState < m_rEnd) && !(m_rEnd < *pHeadState)) {
bCanSolve = true;
iSteps = pHeadState->iSteps;
vecSolve.push_back(pHeadState);
const CState *lastState = pHeadState->pparent;
while(lastState != NULL) {
vecSolve.push_back(lastState);
lastState = lastState->pparent;
}
break;
}
vector<CState*> nextState = pHeadState->__getNextState();
int len = nextState.size(); for(int i = ; i < len; ++i) {
auto state_it = setState.find(nextState[i]);
if(state_it == setState.end()) {
queState.push(nextState[i]);
setState.insert(nextState[i]);
} else {
if((*state_it)->astar_f() > nextState[i]->astar_f()) {
pointerWaitToDelete.insert(*state_it); // 这一句要放在setState.erase前面,防止迭代器失效
setState.erase(state_it);
setState.insert(nextState[i]);
queState.push(nextState[i]);
} else {
delete nextState[i];
}
}
}
if(setState.size() > * ) {
break ;
}
}
iTotalStates = setState.size();
lRunTime = _time.costTime();
setState.erase(&m_rStart);
For(vec_it, vecSolve.begin(), vecSolve.end()) {
setState.erase(*vec_it);
}
For(s_it, setState.begin(), setState.end()) {
delete *s_it;
}
} CAstar::~CAstar()
{
For(vec_it, vecSolve.begin(), vecSolve.end()) {
if(*vec_it != &m_rStart && *vec_it != &m_rEnd) {
delete *vec_it;
}
}
for(const auto &pState: pointerWaitToDelete) {
delete pState;
}
}

  主搜索函数里是以 广度优先搜索 + 优先队列 来实现A*算法的,因为是用多态来实现,用到了指针,所以有些细节可能写得不是很好看,但是经运行测试过没有明显的bug,cpu和内存的使用均在正常的范围内。

  以上两个类就是A*算法的主体框架了,但里面用到了自定义的异常类 CException 和计时类 CTimeVal 等一些工具类,具体代码可以在后面的 github 地址里看到。

  然后是业务相关的类,这里首先是八数码问题的类 CChess,头文件 chess.h:

#ifndef  __CCHESS_H
#define __CCHESS_H #include "state.h"
#include <iostream>
#include <string>
#include <vector>
using std::string;
using std::vector;
using std::ostream; class CChess: public CState
{
friend ostream& operator << (ostream &, const CChess &);
static int iLimitNum;
public:
CChess(const string &state, int row, int col, const string &standard=""); virtual bool operator < (const CState &) const;
virtual void checkSomeFields(const CState &) const; const string& getStrState() const;
void setStrStandard(const string &);
virtual vector<CState*> getNextState() const;
// virtual long astar_f() const;
virtual long astar_g() const;
// virtual long astar_h() const; private:
void check_row_col() const;
void check_value() const;
void check_standard() const;
inline int countNotMatch() const;
inline int countLocalNotMatch(int, int) const; private:
string strState;
int iRow, iCol;
int iZeroIdx;
string strStandard;
int iNotMatch; public:
int iMoveFromLast;
static const string directs[];
enum DIRECT
{
UP, DOWN, LEFT, RIGHT, UNKOWN
};
void output(ostream &out, const string &colSpace=" ", const string &rowSpace="\n") const;
}; #endif

chess.h

源文件 chess.cpp:

#include "chess.h"
#include "exception.h"
#include <cstring>
#include <algorithm>
using std::sort;
using std::swap;
#define For(i,s,t) for(auto i = (s); i != (t); ++i) int CChess::iLimitNum = ;
const string CChess::directs[] = {"up", "down", "left", "right", "unkown"}; void CChess::check_row_col() const
{
if(iRow <= || iCol <= ) {
throw CException(, "行或列的值不能小于等于0!");
}
if(iRow * iCol > iLimitNum) {
char msg[];
sprintf(msg, "行列数的乘积不能超过%d!", iLimitNum);
throw CException(, msg);
}
if(iRow * iCol != strState.size()) {
throw CException(, "行列数的乘积应该和字符串的长度相等!");
}
} void CChess::check_value() const
{
if(iZeroIdx == string::npos) {
throw CException(, "字符串值不合法,应该含有'0'!");
}
bool ch[];
memset(ch, , sizeof(ch));
int len = strState.size();
for(int i = ; i < len; ++i) {
if(ch[strState[i]] == true) {
throw CException(, "字符串中不能含有相同的字符!");
}
ch[strState[i]] = true;
}
} void CChess::check_standard() const
{
int len = strState.size();
int len2 = strStandard.size();
if(len != len2) {
throw CException(, "目标状态的字符长度与当前状态的字符长度不等!");
}
bool origin[];
memset(origin, false, sizeof origin);
For(i, , len) {
origin[strState[i]] = true;
}
bool standard[];
memset(standard, false, sizeof standard);
For(i, , len) {
standard[strStandard[i]] = true;
}
For(i, , ) {
if(origin[i] != standard[i]) {
throw CException(, "目标状态的字符内容与当前状态的字符内容不等!");
}
}
} CChess::CChess(const string &state, int row, int col, const string &standard):
strState(state), iRow(row), iCol(col), CState(),
iMoveFromLast(UNKOWN), strStandard(standard)
{
check_row_col();
iZeroIdx = strState.find('');
check_value();
if(strStandard == "") {
strStandard = strState;
sort(strStandard.begin(), strStandard.end());
}
check_standard();
iNotMatch = countNotMatch();
} void CChess::checkSomeFields(const CState &rhs) const
{
if(iRow != ((CChess*)&rhs)->iRow) {
throw CException(, "开始字符串和结束字符串的行不相同!");
}
if(iCol != ((CChess*)&rhs)->iCol) {
throw CException(, "开始字符串和结束字符串的列不相同!");
}
auto tmp_this = strState;
auto tmp_rhs = ((CChess*)&rhs)->strState;
sort(tmp_this.begin(), tmp_this.end());
sort(tmp_rhs.begin(), tmp_rhs.end());
if(tmp_this != tmp_rhs) {
throw CException(, "开始字符串和结束字符串含有的字符有差别!");
}
} bool CChess::operator < (const CState &rhs) const
{
const auto &r_str = ((CChess*)&rhs)->strState;
int cmp = strcmp(strState.c_str(), r_str.c_str());
const auto &r_row = ((CChess*)&rhs)->iRow;
const auto &r_col = ((CChess*)&rhs)->iCol;
if(cmp == ) {
if(iRow == r_row) return iCol < r_col;
return iRow < r_row;
}
return cmp < ;
} const string& CChess::getStrState() const
{
return strState;
} void CChess::setStrStandard(const string &standard)
{
strStandard = standard;
check_standard();
iNotMatch = countNotMatch();
} int CChess::countNotMatch() const
{
int notMatch = ;
For(i, , iRow) {
For(j, , iCol) {
if(strState[i * iCol + j] != strStandard[i * iCol + j]) {
++notMatch;
}
}
}
return notMatch;
} int CChess::countLocalNotMatch(int one, int two) const
{
int oldNotMatch = ;
if(strState[two] != strStandard[one]) {
++oldNotMatch;
}
if(strState[one] != strStandard[two]) {
++oldNotMatch;
}
int nowNotMatch = ;
if(strState[one] != strStandard[one]) {
++nowNotMatch;
}
if(strState[two] != strStandard[two]) {
++nowNotMatch;
}
return this->iNotMatch - oldNotMatch + nowNotMatch;
} vector<CState*> CChess::getNextState() const
{
vector<CState*> nextChess;
// 0上面存在数字,可以下移
if(iZeroIdx >= iCol) {
CChess *down = new CChess(*this);
swap(down->strState[iZeroIdx - iCol], down->strState[iZeroIdx]);
down->iNotMatch = down->countLocalNotMatch(iZeroIdx - iCol, iZeroIdx);
down->iZeroIdx -= iCol;
down->iMoveFromLast = CChess::DOWN;
nextChess.push_back(down);
}
if(iZeroIdx < strState.size() - iCol) {
CChess *up = new CChess(*this);
swap(up->strState[iZeroIdx + iCol], up->strState[iZeroIdx]);
up->iNotMatch = up->countLocalNotMatch(iZeroIdx + iCol, iZeroIdx);
up->iZeroIdx += iCol;
up->iMoveFromLast = CChess::UP;
nextChess.push_back(up);
}
if(iZeroIdx % iCol != ) {
CChess *right = new CChess(*this);
swap(right->strState[iZeroIdx - ], right->strState[iZeroIdx]);
right->iNotMatch = right->countLocalNotMatch(iZeroIdx - , iZeroIdx);
--right->iZeroIdx;
right->iMoveFromLast = CChess::RIGHT;
nextChess.push_back(right);
}
if((iZeroIdx + ) % iCol != ) {
CChess *left = new CChess(*this);
swap(left->strState[iZeroIdx + ], left->strState[iZeroIdx]);
left->iNotMatch = left->countLocalNotMatch(iZeroIdx + , iZeroIdx);
++left->iZeroIdx;
left->iMoveFromLast = CChess::LEFT;
nextChess.push_back(left);
}
return nextChess;
} long CChess::astar_g() const
{
return iNotMatch;
} void CChess::output(ostream &out, const string &colSpace, const string &rowSpace) const
{
for(int i = ; i < iRow; ++i) {
for(int j = ; j < iCol; ++j) {
out << strState[i * iCol + j];
if(j != iCol - ) {
out << colSpace;
}
}
if(i != iRow - ) {
out << rowSpace;
}
}
} std::ostream& operator << (std::ostream &out, const CChess &oChess)
{
oChess.output(out);
out << "\n";
return out;
}

chess.cpp

  八数码问题当时是花了挺久时间做了很大的优化的,最后是main函数,用于简单的交互功能:

#include "chess.h"
#include "exception.h"
#include "astar.h"
#include "timeval.h"
#include <unistd.h>
#include <string.h>
#include <iostream>
using namespace std; int main(int argc, char const *argv[])
{
string str;
int r, c;
while(true) {
try {
cout << "please input the start state(string) and row, col, separate with a space:\n";
if(bool(cin >> str >> r >> c) == false) {
break;
}
CChess start(str, r, c);
cout << "please input the end state(string) and row, col, separate with a space:\n";
if(bool(cin >> str >> r >> c) == false) {
break;
}
CChess end(str, r, c);
start.setStrStandard(str); cout << "Your game is:\n" << start << " --->\n" << end << "\n"; CAstar game(start, end);
game.run(); if(game.bCanSolve == true) {
cout << "your game can be solve:\n";
cout << "the total steps is: " << game.iSteps << "\n";
cout << "and the path is:\n";
int len = game.vecSolve.size();
cout << *((CChess*)(game.vecSolve[len - ])) << "\n";
for(int i = len - ; i >= ; --i) {
cout << " |\n";
cout << " | " << CChess::directs[((CChess*)(game.vecSolve[i]))->iMoveFromLast] << "\n";
cout << " \\|/\n\n";
cout << *((CChess*)(game.vecSolve[i])) << "\n";
}
} else {
cout << "sorry, your game can't be solve, please input the other state.\n\n";
}
cout << " and the max states is: " << game.iTotalStates << "\n";
cout << " and the runtime is: " << game.lRunTime << "\n"; } catch (const CException &ex) {
cerr << ex.code << ": " << ex.msg << "\n";
} catch (...) {
break;
}
}
return ;
}

main.cpp

  还写了个用于生成测试用例的程序:

#include "astar.h"
#include "chess.h"
#include "exception.h"
#include <string>
#include <iostream>
#include <sstream>
#include <cctype>
#include <algorithm>
using namespace std; void usage(const string &exe_name)
{
string echo = "Usage: " + exe_name + " start(string) row(positive int) col(positive int) steps(positive int).";
cout << echo << "\n";
} template <typename T>
T strTo(const string &str)
{
stringstream ss;
ss << str;
T ret;
ss >> ret;
return ret;
} int main(int argc, char const *argv[])
{
if(argc < ) {
usage(argv[]);
exit();
}
int row = strTo<int>(argv[]);
int col = strTo<int>(argv[]);
if(!row || !col) {
usage(argv[]);
exit();
}
int steps = strTo<int>(argv[]);
string strStandard(argv[]);
bool outputOne = true;
if(argc >= ) {
outputOne = false;
}
try {
CChess start(strStandard, row, col);
auto setChess = CAstar::getStateByStartAndSteps(start, steps);
if(setChess.size() == ) {
throw CException(, "走不了这么多步!");
}
if(outputOne == true) {
auto first = *setChess.begin();
((CChess*)first)->output(cout);
cout << "\t\t" << ((CChess*)first)->getStrState() << "\n";
}
else {
cout << "setChess.size() = " << setChess.size() << "\n";
for_each(setChess.begin(), setChess.end(), [](const CState *elem) {
((CChess*)elem)->output(cout);
cout << "\t\t" << ((CChess*)elem)->getStrState() << "\n";
});
}
for_each(setChess.begin(), setChess.end(), [](const CState *elem){
delete elem;
});
} catch(const CException &ex) {
cerr << ex.code << ": " << ex.msg << "\n";
exit();
} catch(...) {
cerr << "unkown error.\n";
exit();
}
return ;
}

rand_init.cpp

  makefile文件:

CC = g++
COMOPT = -std=c++ INCLUDEDIR = -I./tools LIBDIR = -L./tools
LIBS = -ltools
LINK = $(LIBDIR) $(LIBS) # OBJS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
OBJS += chess.o astar.o state.o OUTPUT += game rand_init all: $(OUTPUT) game: $(OBJS) main.o
make -C tools
$(CC) -o $@ $^ $(LINK) rand_init: $(OBJS) rand_init.o
make -C tools
$(CC) -o $@ $^ $(LINK) %.o: %.cpp
$(CC) -o $@ -c $< $(COMOPT) $(INCLUDEDIR) clean:
make clean -C tools
rm -f *.o
rm -f $(OUTPUT)

makefile

  完整的八数码问题程序:astar_EightDigital

  然后是传教士过河问题,CState 类和 CAstar 类和上面一样,具体的业务实现类 CPersonState 如下:

#ifndef  __PERSON_H
#define __PERSON_H #include "state.h"
#include <iostream>
using std::ostream; class CPersonState: public CState
{
friend ostream& operator << (ostream&, const CPersonState&);
public:
CPersonState();
virtual bool operator < (const CState &) const;
virtual vector<CState*> getNextState() const;
virtual long astar_g() const;
void init(int, int, int);
public:
static int iTotalMissionary; // the total number of missionaries
static int iTotalSavage; // the total number of savages
static int iBoatCapacity; // the capacity of the boat
private:
int iMissionary; // the number of missionaries in the shore where boat anchors
int iSavage; // the number of savages in the shore where boat anchors
int iBoatPosition; // the position of boat, this shore or opposite shore
public:
enum POSITION
{
THIS_SHORE = , OPPOSITE_SHORE
};
int iMoveMissionary;
int iMoveSavage;
}; #endif

person.h

#include "person.h"
#include "exception.h"
#include <algorithm>
using std::min;
using std::max;
#define For(i,s,t) for(auto i = (s); i != (t); ++i) int CPersonState::iTotalMissionary = -;
int CPersonState::iTotalSavage = -;
int CPersonState::iBoatCapacity = -; CPersonState::CPersonState() {} void CPersonState::init(int _m, int _s, int _b)
{
iMissionary = _m;
iSavage = _s;
iBoatPosition = _b;
iMoveMissionary = iMoveSavage = ; if(iTotalMissionary == -) {
throw CException(, "the total number of missionaries has not been initialized.");
}
if(iTotalSavage == -) {
throw CException(, "the total number of savages has not been initialized.");
}
if(iBoatCapacity == -) {
throw CException(, "the capacity of the boat has not been initialized.");
}
if(iMissionary > iTotalMissionary) {
throw CException(, "the number of missionaries on this shore exceeded the total number.");
}
if(iSavage > iTotalSavage) {
throw CException(, "the number of savages on this shore exceeded the total number.");
}
if(iMissionary && iMissionary < iSavage) {
throw CException(, "the number of missionaries can\'t be less then the number of savages.");
}
if(iBoatPosition != CPersonState::THIS_SHORE && iBoatPosition != CPersonState::OPPOSITE_SHORE) {
throw CException(, "the value of iBoat is invalid, which must be CPersonState::THIS_SHORE \
or CPersonState::OPPOSITE_SHORE, you can use or certainly.");
}
} bool CPersonState::operator < (const CState &rhs) const
{
const CPersonState* const prhs = (CPersonState*)&rhs;
if(iMissionary == prhs->iMissionary) {
if(iSavage == prhs->iSavage) {
return iBoatPosition < prhs->iBoatPosition;
}
return iSavage < prhs->iSavage;
}
return iMissionary < prhs->iMissionary;
} using std::cin;
using std::cout; vector<CState*> CPersonState::getNextState() const
{
vector<CState*> nextState;
int oppo_m = iTotalMissionary - iMissionary;
int oppo_s = iTotalSavage - iSavage;
int mk = min(iMissionary, iBoatCapacity);
int sk = min(iSavage, iBoatCapacity);
For(x, , mk + ) {
For(y, , sk + ) {
if(!x && !y) continue;
if(iMissionary - x != && iMissionary - x < iSavage - y) continue;
if(x + y > iBoatCapacity || (x && y > x) ) break;
if(oppo_m + x != && oppo_m + x < oppo_s + y) break;
CPersonState *_next = new CPersonState();
// _next->init(iMissionary - x, iSavage - y, 3 - iBoatPosition);
_next->init(oppo_m + x, oppo_s + y, - iBoatPosition);
_next->iMoveMissionary = x;
_next->iMoveSavage = y;
nextState.push_back(_next);
}
}
return nextState;
} long CPersonState::astar_g() const
{
int remain_num;//, transport_num;
if(iBoatPosition == CPersonState::THIS_SHORE) {
remain_num = iMissionary + iSavage;
} else {
remain_num = (iTotalMissionary - iMissionary) + (iTotalSavage - iSavage);
}
return remain_num;// + transport_num;
} ostream& operator << (ostream &out, const CPersonState &state)
{
out << "(" << state.iMissionary << ", " << state.iSavage << ", " << state.iBoatPosition << ")";
return out;
}

person.cpp

  main 函数:

#include "person.h"
#include "astar.h"
#include "exception.h"
#include <iostream>
using namespace std; int main(int argc, char const *argv[])
{
int m,s,k;
while(true) {
cout << "please input the number of missionaries, savages and the capacity of the boat, separate with a space:\n";
if(bool(cin >> m >> s >> k) == false) {
break;
}
CPersonState::iTotalMissionary = m;
CPersonState::iTotalSavage = s;
CPersonState::iBoatCapacity = k;
CPersonState start, end;
try {
start.init(m, s, CPersonState::THIS_SHORE);
end.init(m, s, CPersonState::OPPOSITE_SHORE);
} catch(const CException &ex) {
cerr << ex.code << ": " << ex.msg << "\n";
} catch(...) {
break;
}
CAstar game(start, end);
game.run();
if(game.bCanSolve == true) {
cout << "your game can be solve:\n";
cout << "the total steps is: " << game.iSteps << "\n";
cout << "and the path is:\n";
int len = game.vecSolve.size();
cout << *((CPersonState*)(game.vecSolve[len - ])) << "\n";
for(int i = len - ; i >= ; --i) {
cout << "\n |\n";
auto pstate = (CPersonState*)(game.vecSolve[i]);
cout << " | (" << pstate->iMoveMissionary << ", " << pstate->iMoveSavage << ")\n";
cout << " \\|/\n\n";
cout << *((CPersonState*)(game.vecSolve[i])) << "\n";
}
} else {
cout << "sorry, your game can't be solve, please input another state.\n\n";
}
cout << "the total steps is: " << game.iSteps << "\n";
cout << "and the max states is: " << game.iTotalStates << "\n";
cout << "and the runtime is: " << game.lRunTime << "\n";
}
return ;
}

main.cpp

  makefile 文件(和上面的相似,只是编译的目标项稍有不同):

CC = g++
COMOPT = -std=c++ -g INCLUDEDIR = -I./tools LIBDIR = -L./tools
LIBS = -ltools
LINK = $(LIBDIR) $(LIBS) # OBJS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
OBJS += person.o astar.o state.o OUTPUT += across_river all: $(OUTPUT) across_river: $(OBJS) main.o
make -C tools
$(CC) -o $@ $^ $(LINK) %.o: %.cpp
$(CC) -o $@ -c $< $(COMOPT) $(INCLUDEDIR) clean:
make clean -C tools
rm -f *.o
rm -f $(OUTPUT)

makefile

  之后我用 Java 来重写,除了面向对象的语法有区别以外其它都几乎是一样的:

首先是自定义异常类 MyException:

package tools;

/**
* 自定义的异常类,错误码和错误信息的简单封装
*/
public class MyException extends RuntimeException { private static final long serialVersionUID = 1L; public int code; // 错误码
public String msg; // 错误信息 public MyException(int code, String msg) {
super();
this.code = code;
this.msg = msg;
} @Override
public String toString() {
return "MyException [code=" + code + ", msg=" + msg + "]";
} }

MyException.java

计时类 TimeValue:

package tools;

public class TimeValue {

    private long milliSecond;

    /**
* 初始化时获取当前系统时间(millisecond)
*/
public TimeValue() {
super();
this.milliSecond = System.currentTimeMillis();
} public TimeValue(long milliSecond) {
super();
this.milliSecond = milliSecond;
} /**
* 返回耗时,以毫秒为单位
*/
public long costTime() {
long nowMilliSecond = System.currentTimeMillis();
return nowMilliSecond - this.milliSecond;
} /**
* 重置时间为当前时间
*/
public void reset() {
this.milliSecond = System.currentTimeMillis();
} @Override
public String toString() {
return "TimeValue [milliSecond=" + milliSecond + "]";
} }

TimeValue.java

抽象类 State:

package main;

import java.util.ArrayList;

import tools.MyException;

/**
* 表示状态的抽象类
*/
public abstract class State { public int steps;
public State parent; public State() {
super();
this.steps = 0;
this.parent = null;
} abstract public int hashCode(); abstract public boolean equals(Object obj); public void checkSomeFields(State rhs) throws MyException {} abstract public ArrayList<State> getNextState(); public ArrayList<State> __getNextState() {
ArrayList<State> nextState = this.getNextState();
for(State st: nextState) {
st.steps = this.steps + 1;
st.parent = this;
}
return nextState;
} public long astar_f() {
return this.steps;
} abstract public long astar_g(); public long astar_h() {
return this.astar_f() + this.astar_g();
}
}

A*算法类 Astar:

package main;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
import java.util.Comparator;
import java.util.HashMap; import tools.MyException;
import tools.TimeValue; /**
* astar 算法主体类
*/
public class Astar { public State start;
public State end;
public boolean canSolve;
public int steps;
public ArrayList<State> vecSolve;
public long runTime;
public int totalStates; public Astar(State start, State end) throws MyException {
super();
this.start = start;
this.end = end;
this.canSolve = false;
this.steps = 0;
this.vecSolve = new ArrayList<State>();
this.runTime = 0;
this.totalStates = 0;
start.checkSomeFields(end);
} static Set<State> getStateByStartAndSteps(State start, int steps) {
Set<State> retSet = new HashSet<>();
// 以后再补充,懒得把c++代码翻译了
return retSet;
} void run() {
TimeValue _time = new TimeValue();
Map<State, State> mapState = new HashMap<>();
mapState.put(this.start, this.start);
// 最小堆
Queue<State> queState = new PriorityQueue<>(new Comparator<State>() {
@Override
public int compare(State o1, State o2) {
long diff = o1.astar_h() - o2.astar_h();
return diff == 0 ? 0: (diff > 0 ? 1: -1);
}
});
queState.add(this.start); while(!queState.isEmpty()) {
State headState = queState.poll();
if(headState.equals(this.end)) {
this.canSolve = true;
this.steps = headState.steps;
this.vecSolve.add(headState);
State lastState = headState.parent;
while(lastState != null) {
this.vecSolve.add(lastState);
lastState = lastState.parent;
}
break;
}
ArrayList<State> nextState = headState.__getNextState();
for(State _next: nextState) {
State state = mapState.get(_next);
if(state == null) {
queState.add(_next);
mapState.put(_next, _next);
} else {
if(state.astar_f() > _next.astar_f()) {
mapState.remove(_next);
mapState.put(_next, _next);
queState.add(_next);
}
}
}
if(mapState.size() > 3000 * 10000) {
break;
}
}
this.totalStates = mapState.size();
this.runTime = _time.costTime();
}
}

用于表示传教士过河状态的具体类 PersonState:

package main;

import java.util.ArrayList;

import tools.MyException;

public class PersonState extends State {

    static public int totalMissionary;    // the total number of missionaries
static public int totalSavage; // the total number of savages
static public int boatCapacity; // the capacity of the boat private int missionary; // the number of missionaries in the shore where boat anchors
private int savage; // the number of savages in the shore where boat anchors
private int boatPosition; // the position of boat, this shore or opposite shore public static final int THIS_SHORE = 1;
public static final int OPPOSITE_SHORE = 2; public int moveMissionary;
public int moveSavage; public PersonState() {
super();
} public void init(int _m, int _s, int _b) throws MyException {
this.missionary = _m;
this.savage = _s;
this.boatPosition = _b;
this.moveMissionary = moveSavage = 0; if(totalMissionary == -1) {
throw new MyException(101, "the total number of missionaries has not been initialized.");
}
if(totalSavage == -1) {
throw new MyException(102, "the total number of savages has not been initialized.");
}
if(boatCapacity == -1) {
throw new MyException(103, "the capacity of the boat has not been initialized.");
}
if(missionary > totalMissionary) {
throw new MyException(104, "the number of missionaries on this shore exceeded the total number.");
}
if(savage > totalSavage) {
throw new MyException(105, "the number of savages on this shore exceeded the total number.");
}
if(missionary != 0 && missionary < savage) {
throw new MyException(106, "the number of missionaries can\'t be less then the number of savages.");
}
if(boatPosition != THIS_SHORE && boatPosition != OPPOSITE_SHORE) {
throw new MyException(107, "the value of iBoat is invalid, which must be CPersonState::THIS_SHORE or CPersonState::OPPOSITE_SHORE, you can use 1 or 2 certainly.");
}
} @Override
public String toString() {
return "PersonState [missionary=" + missionary + ", savage=" + savage + ", boatPosition=" + boatPosition
+ ", moveMissionary=" + moveMissionary + ", moveSavage=" + moveSavage + "]";
} @Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + boatPosition;
result = prime * result + missionary;
result = prime * result + moveMissionary;
result = prime * result + moveSavage;
result = prime * result + savage;
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PersonState other = (PersonState) obj;
if (boatPosition != other.boatPosition)
return false;
if (missionary != other.missionary)
return false;
if (savage != other.savage)
return false;
return true;
} @Override
public ArrayList<State> getNextState() {
ArrayList<State> nextState = new ArrayList<>();
int oppo_m = totalMissionary - missionary;
int oppo_s = totalSavage - savage;
int mk = Math.min(missionary, boatCapacity);
int sk = Math.min(savage, boatCapacity);
for(int x = 0; x <= mk; ++x) {
for(int y = 0; y <= sk; ++y) {
if(x == 0 && y == 0) continue;
if(missionary - x != 0 && missionary - x < savage - y) continue;
if(x + y > boatCapacity || (x != 0 && y > x) ) break;
if(oppo_m + x != 0 && oppo_m + x < oppo_s + y) break;
PersonState _next = new PersonState();
_next.init(oppo_m + x, oppo_s + y, 3 - boatPosition);
_next.moveMissionary = x;
_next.moveSavage = y;
nextState.add(_next);
}
}
return nextState;
} @Override
public long astar_g() {
int remain_num;//, transport_num;
if(boatPosition == THIS_SHORE) {
remain_num = missionary + savage;
} else {
remain_num = (totalMissionary - missionary) + (totalSavage - savage);
}
return remain_num;// + transport_num;
} }

PersonState.java

最后是 main 函数,实现简单的交互:

package main;

import java.util.Scanner;

import tools.MyException;

public class Main {

    public static void main(String[] args) {
int m = 0, s = 0, k = 0;
Scanner cin = new Scanner(System.in);
while(true) {
System.out.println("please input the number of missionaries, savages and the capacity of the boat, separate with a space:");
try {
m = cin.nextInt();
s = cin.nextInt();
k = cin.nextInt();
} catch(Exception ex) {
System.out.println(ex.toString() + "\nbye~");
break;
}
PersonState.totalMissionary = m;
PersonState.totalSavage = s;
PersonState.boatCapacity = k;
PersonState start = new PersonState();
PersonState end = new PersonState();
try {
start.init(m, s, PersonState.THIS_SHORE);
end.init(m, s, PersonState.OPPOSITE_SHORE);
} catch (MyException ex) {
System.out.println(ex.toString());
} catch(Exception ex) {
break;
}
Astar game = new Astar(start, end);
game.run();
if(game.canSolve == true) {
System.out.println("your game can be solve:");
System.out.println("the total steps is: " + game.steps);
System.out.println("and the path is:\n");
int len = game.vecSolve.size();
System.out.println(game.vecSolve.get(len - 1).toString());
for(int i = len - 2; i >= 0; --i) {
System.out.println("\n |");
PersonState personState = (PersonState)game.vecSolve.get(i);
System.out.println(" | (" + personState.moveMissionary + ", " + personState.moveSavage + ")");
System.out.println(" \\|/\n");
System.out.println(personState.toString());
}
System.out.println();
} else {
System.out.println("sorry, your game can't be solve, please input another state.\n");
}
System.out.println("the total steps is: " + game.steps);
System.out.println("and the max states is: " + game.totalStates);
System.out.println("and the runtime is: " + game.runTime + "\n");
}
cin.close();
} }

main.java

  完整的传教士过河代码:missionary_across_river

A*算法 -- 八数码问题和传教士过河问题的代码实现的更多相关文章

  1. noj 算法 八数码问题

    描述 在九宫格里放在1到8共8个数字还有一个是空格,与空格相邻的数字可以移动到空格的位置,问给定的状态最少需要几步能到达目标状态(用0表示空格):1 2 34 5 67 8 0   输入 输入一个给定 ...

  2. HDU 1043 八数码&lpar;A&ast;搜索)

    在学习八数码A*搜索问题的时候须要知道下面几个点: Hash:利用康托展开进行hash 康托展开主要就是依据一个序列求这个序列是第几大的序列. A*搜索:这里的启示函数就用两点之间的曼哈顿距离进行计算 ...

  3. A&ast;算法解决八数码问题 Java语言实现

    0X00 定义 首先要明确一下什么是A*算法和八数码问题? A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法也是一种启发性的算法,也是解决许多搜索问题的有效算法.算法中的距离估 ...

  4. HUD 1043 Eight 八数码问题 A&ast;算法 1667 The Rotation Game IDA&ast;算法

    先是这周是搜索的题,网站:http://acm.hdu.edu.cn/webcontest/contest_show.php?cid=6041 主要内容是BFS,A*,IDA*,还有一道K短路的,.. ...

  5. HDU 1043 Eight 八数码问题 A&ast;算法(经典问题)

    HDU 1043 Eight 八数码问题(经典问题) 题意 经典问题,就不再进行解释了. 这里主要是给你一个状态,然后要你求其到达\(1,2,3,4,5,6,7,8,x\)的转移路径. 解题思路 这里 ...

  6. Java实现 蓝桥杯 算法提高 八数码(BFS)

    试题 算法提高 八数码 问题描述 RXY八数码 输入格式 输入两个33表格 第一个为目标表格 第二个为检索表格 输出格式 输出步数 样例输入 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 ...

  7. 八数码难题之 A&ast; 算法

    人生第一个A*算法-好激动-- 八数码难题--又称八数码水题,首先要理解一些东西: 1.状态可以转化成整数,比如状态: 1 2 3 4 5 6 7 8 0 可以转化成:123456780这个整数 2. ...

  8. 【C&plus;&plus;算法设计】八数码问题

    八数码问题 [题意] 编好为1~8的8个正方形滑块摆成3行3列(一个格子为空),如图所示 每次可以移动空格相邻的滑块到空格,要计算出能移动出目标局面的最小步数,如无法达到则输出-1. [分析] 我们可 ...

  9. 【算法】BFS&plus;哈希解决八数码问题

    15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖“X”; 拼图的目的是安排 ...

随机推荐

  1. 备忘:用fiddler搭一个请求响应器

    最近工作中发现美国的服务器响应慢,影响工作效率.就在想办法.突然发现fiddler可以帮我解决这个问题.AutoResponder功能可以帮我们做到.在其中设置一些请求,比如:http://xxxx. ...

  2. webapi 通过dynamic 接收可变参数

    public class JsonParamModel { /// <summary> /// json key /// </summary> public string Js ...

  3. Timberwolves forward Kevin Garnett to retire &lowbar;洛杉矶时报

    Timerwolves:森林狼队,forward:前锋; kevin Garnett,the best player in Minnesota Timberwolves history,is expe ...

  4. word2013中取消句首字母自动大写

    经常使用word的朋友都知道word中一行的首字母会自动大写,这给用户带来方便的同时,也产生了问题,因为有时候我们并不希望每行开头的首字母大写.要取消首字母自动大写可以取消勾选"首句字母大写 ...

  5. AI之旅(4):初识逻辑回归

    前置知识   求导 知识地图   逻辑回归是用于分类的算法,最小的分类问题是二元分类.猫与狗,好与坏,正常与异常.掌握逻辑回归的重点,是理解S型函数在算法中所发挥的作用,以及相关推导过程. 从一个例子 ...

  6. Charles破解网站收藏(持续更新)

    1. 在这个网站(http://charles.iiilab.com/)下载破解文件 charles.jar 2. 替换掉原文件夹里的charles.jar Mac: /Applications/Ch ...

  7. while循环、break、continue

    我们通过while循环让python循环进行操作 break 跳出整个循环 continue 终止当前循环并不再继续往下执行,回到开头开始继续循环 下面会详细解释一下,例如: 1 a = 1 2 wh ...

  8. win10无法访问局域网共享文件?解决如此简单。。。。。

    1 笔记本系统win10 X64企业版,其中一文件夹已设置为“共享”.本地帐号登录系统. 2 平板电脑系统win8.1 X64专业版,可以顺畅的访问笔记本的共享文件.微软帐号登录系统. 3 平板电脑系 ...

  9. 160321、ORACLE分页查询SQL语法——最高效的分页

    --1:无ORDER BY排序的写法.(效率最高) --(经过测试,此方法成本最低,只嵌套一层,速度最快!即使查询的数据量再大,也几乎不受影响,速度依然!) SELECT *   FROM (SELE ...

  10. 成都Uber优步司机奖励政策(3月3日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...