需要介绍IDA* 吗?
这个链接讲的还可以,其实自己搜可以看到更多blog介绍这个算法
《人工智能-一种现代的方法》第三章【有信息搜索part】
A*:
采用广度优先搜索策略,在搜索过程中使用启发函数,即有大致方向的向前进虽然目标有时候不是很明确。关键在于启发函数,启发函数的优劣直接影响A*算法的效率。f(n)=g(n)+h(n);
f(n)表示从初始状态到目标状态的估测代价。
g(n)表示从初始状态到当前状态的代价(已经确定)。
h(n)表示从当前状态到目标状态的估测代价(预测)。
h*(n)表示从当前状态到目标状态的实际代价。
若h(n)<=h*(n),则总能找到最优解。(当h(n)<h*(n)的时候,不可能找到一条从初始状态到达目标状态的路径。在搜索过程中使得h(n)逐渐接近h*(n),最终找到最优路径。
IDA*
迭代加深搜索算法,在搜索过程中采用估值函数,以减少不必要的搜索。
设置每次可达的最大深度depth,若没有到达目标状态则加深最大深度。
采用估值函数,剪掉f(n)大于depth的路径。
使用回溯方法,不用保存中间状态,大大节省了空间。
重复搜索:回溯过程中每次depth变大都要再次从头搜索。
其他和A*差不多
闲聊ing
我玩了几局的感觉就是,无非就是0【空格】在游走。想象一下吧~
作业要求
15-PUZZLE数码。
要求:
1、随机生成两个15-PUZZLE数码棋盘。
2、检查问题是不是有解。如果无解,输出无解。
3、用方式(1)或(2)输出一个解。
(1) 如果有解,输出一个“.txt”文件。文件把解路径上的每张棋盘都按顺序输出。
(3) 或者,写一个带可视化界面的程序,在界面上动态展示解路径。
提示:参考教材第3章。
源代码
1.一开始不直接随机生成矩阵,默认初始矩阵,目标矩阵固定【需要可以自己改】
#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define N 4
#define n 16
#define abs(s) (s>=0?s:-(s))//一定要加括号
const int maxn=100;
const int dx[N]={0,0,1,-1};
const int dy[N]={1,-1,0,0};
const char direction[N]={'R','L','D','U'};
int final_pos[16][2]= {{3,3},
{0,0},{0,1},{0,2},{0,3},
{1,0},{1,1},{1,2},{1,3},
{2,0},{2,1},{2,2},{2,3},
{3,0},{3,1},{3,2}
};
vector<int>ans;
/*example 1
int init[N][N]={
{2,3,4,0},
{1,5,7,8},
{9,6,10,12},
{13,14,11,15}
};
*/
//example 2
int init[N][N]={
{5,1,2,4},
{9,6,3,8},
{13,15,10,11},
{14,0,7,12}
};
int goal[N][N]={
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,0}
};
int cpinit[N][N];
//假设init是随机生成的数码,goal是完成排序的数码
生成随机矩阵
void init_puzzle(){
set<int>nums;
for(int i=0;i<N*N;i++)nums.emplace(i);
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
srand((unsigned)time(NULL));
int pos=rand()%(nums.size());
auto it=nums.begin();
advance(it,pos);
init[i][j]=*it;
nums.erase(it); //for(it=nums.begin();it!=nums.end();it++)cout<<*it<<" "; //it++:符号++被重载了
}
}
nums.clear();
}
2.输出随机的初始矩阵和目标矩阵查看
bool vis[16];
int xx;
int yy;
void prin(int a[][N]){
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
printf("%d ",a[i][j]);
}
printf("\n");
}
printf("\n");
}
void vision(){
int i,j;
printf("The init_puzzle is:\n");
prin(init);
printf("The goal_puzzle is:\n");
prin(goal);
}
3.启发函数:h(x)=当前状态的各个点的曼哈顿距离
int calcu_manhattan(){//评估函数:曼哈顿距离
int res=0,temp=0;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(init[i][j]==0)continue;
temp=init[i][j];
res+=abs(final_pos[temp][0]-i)+abs(final_pos[temp][1]-j);
}
}
return res;
}
4.判断当前这个矩阵是否可以找到解,这个真的很奇妙
/*
只是十五数码和八数码判断是否有解的方法不同:
八数码0的移动不影响其余7个数字逆序数的奇偶性
而十五数码0的左右移动[y]不影响其余15个数逆序数的奇偶性(顺序不变),但上下移动[x]改变奇偶
[16个数逆序数与0的距离之和]s的奇偶性不因0的滑动而改变,初始时s是奇数[0+0+...+15],所以只有s是奇数的状态才是可到达的
原文:https://blog.csdn.net/yan_____/article/details/8219462
*/
bool judge_solution_is_valid(){
mem(vis,0);
int i,tot=0;
xx=-1;
yy=-1;
for(i=0;i<N;i++){//逆序数
for(int j=0;j<N;j++){
if(!init[i][j]){
xx=i,yy=j;
continue;
}
for(int k=1;k<init[i][j];k++){
if(!vis[k])tot++;
}
vis[init[i][j]]=1;
}
}
if(xx<0)return false;
//printf("tot=%d xx=%d yy=%d\n",tot,xx,yy);
tot+=xx;//上下移动[x]改变奇偶
if(tot&1)return true;
return false;
}
5.IDA*的写法
bool dfs(int g,int prev_direction,int bound){
int h=calcu_manhattan();
if(h==0)return true;
if((g+h)>bound)return false;//剪枝
for(int i=0;i<N;i++){//遍历方向
//绝妙的写法:不回头的写法:i^1!=i
if(i==(prev_direction^1))continue;
int nowx=xx+dx[i];
int nowy=yy+dy[i];
if(nowx<0||nowx>=4)continue;
if(nowy<0||nowy>=4)continue;
ans.push_back(i);
swap(init[nowx][nowy],init[xx][yy]);
swap(xx,nowx);swap(yy,nowy);
if(dfs(g+1,i,bound))return true;
//回溯
swap(xx,nowx);swap(yy,nowy);
swap(init[nowx][nowy],init[xx][yy]);
ans.pop_back();
}
return false;
}
bool ida_star(){
int limit=calcu_manhattan();
for(;limit<=maxn;limit++){
if(dfs(0,-1,limit))return true;
}
return false;
}
6.输出过程
void display_graph(){//矩阵输出
printf("\n\n");
prin(cpinit);
for(int i=0;i<ans.size();i++){
int inx=ans[i];
int nowx=xx+dx[inx];
int nowy=yy+dy[inx];
swap(cpinit[nowx][nowy],cpinit[xx][yy]);
prin(cpinit);
xx=nowx;
yy=nowy;
}
}
void display_str(){//字符串输出-UVA10181的形式
for(int i=0;i<ans.size();i++){
int inx=ans[i];
printf("%c",direction[inx]);
}
printf("\n");
}
7.函数调用
int main(){
//init_puzzle();
//printf("\n\n");prin(init);
vision();
ans.clear();
if(judge_solution_is_valid()){
int x0=xx,y0=yy;
memcpy(cpinit,init,sizeof(init));
if(ida_star()){
xx=x0;
yy=y0;
printf("\n\nThe solution is %d steps\n",ans.size());
display_graph();
}
else printf("This puzzle is not solvable.\n");
}
else printf("This puzzle is not solvable.\n");
return 0;
}
reference
//参考:
//https://blog.csdn.net/lph188/article/details/86504502
//http://www.hankcs.com/program/algorithm/uva-10181-15-puzzle-problem.html
//题目测试:UVa 10181 https://vjudge.net/problem/UVA-10181
注意
以上main函数不可以直接用于交题,需要修改
结果
1.UVA10181结果
2.example2的结果
3.随机矩阵的部分结果【自己跑试试就知道了】