AI作业-IDA*-15-puzzle(UVA 10181)

需要介绍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结果
UVA10181结果
2.example2的结果
example2的结果
example2的结果
3.随机矩阵的部分结果【自己跑试试就知道了】
随机数的结果

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值