IDA*算法, ID(Iterative Deepening)指的是迭代加深. 它的思想是重复进行限制最大深度的深度优先搜索(此限制从某个最小值遍历到最大值), 也称为深度受限搜索.

一般情况下, 为了提高搜索速度, 迭代加深不会记录已搜索过的状态, 但同时, 需要做一些调整, 以避免出现马上回溯到上一状态的情况.


IDA*算法的步骤

  1. 首先对初始状态进行评估, 评估值作为最小限度, 而最大限度为自己的设置.
    这个评估值在这个问题中可以用此状态到正确状态的每个位置的曼哈顿距离来表示.

  2. 从最小限度到最大限度进行遍历, 此值作为当前dfs的限度值, 这个限度不断在有效范围内递增的过程就称作迭代加深

  3. 进行dfs, 调整状态, 将新状态加入到新的dfs中, 直到找到了一个解(由于迭代加深, 此解为最优解). 进行回溯, 加入路径, 算法结束.

PS. 如果在限度内都没有找到解, 就输出unsolved.

从上面的分析中可见, 即使是IDA*算法, 其局限性依然很大, 比如它需要设置一个最大限制, 而超出这个限制的状态将无法求解出.


一些解释:

  1. 曼哈顿距离预处理, 每个点在另一个位置的曼哈顿距离16*16
    x坐标距离 abs(i / N - j / N)
    y坐标距离 abs(i % N - j % N)
    曼哈顿距离可以将x坐标和y坐标相互独立开来, 且曼哈顿距离是相对的. 而在上面的表达式中, 可以理解为他们以(0, 0)为参照点;
    abs((i / N - 0 - (j / N - 0))

  2. 状态的定义
    在这个IDA*算法中, 每个状态包含了以下信息.

    1. 16个数的位置
    2. 空格所在位置
    3. 当前状态距离正确状态的曼哈顿距离
      简单的结构体可实现
  3. dfs难道要遍历所有可能的情况? 不, 别忘了我们是迭代加深(Iterative Deepeni)!
    所谓迭代, 就是一代一代更迭, 所以, 既然我们确定了最大范围(LIMIT), 那么我们就可以在这个范围里再设置范围限制, 然后搜索(dfs).
    每当找到了一个解, 这个解就是最优解, 因为更优解在我们之前的搜索中没有出现.

  4. 不会绕圈吗?
    答: 会绕圈, 但是不会有很大影响, 因为我们设置了搜索次数, 所以绕圈多消耗步骤的自然会淘汰掉.

  5. 如何理解sum += MDT[i][pz.f[i] - 1];
    MDT[i][pz.f[i] - 1]这个状态可以理解为, 在第i格位置, 当它的值为pz.f[i]时, 他们的曼哈顿距离之差. 为什么要减一? 因为输入的值为1...15, 而代码中都位置下标都是从0开始的.


IDA*完整代码

#include <iostream>
#include <cmath>using namespace std;
#define N 4
#define N2 16
#define LIMIT 57static const int dx[4] = {0, -1, 0, 1};
static const int dy[4] = {1, 0, -1, 0};
static const char dir[4] = {'r', 'u', 'l', 'd'};
int MDT[N2][N2];struct Puzzle {int f[N2], space, MD; // 位置, 空格, 曼哈顿距离
};Puzzle state;
int limit;
int path[LIMIT];int getALLMD(Puzzle pz) {int sum = 0;for (int i = 0; i < N2; ++i) {if (pz.f[i] == N2) continue;sum += MDT[i][pz.f[i] - 1];}return sum;
}bool isSolved() {for (int i = 0; i < N2; ++i) {if (state.f[i] != i + 1) return false;}return true;
}bool dfs(int depth, int prev) {if (state.MD == 0) return true; // 搜索到了答案.if (depth + state.MD > limit) return false; // 超过当前迭代限制int sx = state.space / N;int sy = state.space % N;Puzzle tmp;for (int r = 0; r < 4; ++r) {int tx = sx + dx[r];int ty = sy + dy[r];if (tx < 0 || ty < 0 || tx >= N || ty >= N) continue;if (max(prev, r) - min(prev, r) == 2) continue; // 妙! 避免迂回. 减少了很多不必要搜索tmp = state;state.MD -= MDT[tx * N + ty][state.f[tx * N + ty] - 1]; // 消除原位置的曼哈顿距离state.MD += MDT[sx * N + sy][state.f[tx * N + ty] - 1]; // 添加新位置的曼哈顿距离, 注意, MDT由非0/16产生swap(state.f[tx * N + ty], state.f[sx * N + sy]);state.space = tx * N + ty;if (dfs(depth + 1, r)) { // 先搜索, 搜索成功后再添加路径. 巧妙, 值得学习path[depth] = r;return true;}state = tmp; // 回溯复原}return false;
}string iterative_deepening(Puzzle in) {in.MD = getALLMD(in);for (limit = in.MD; limit <= LIMIT; limit++) { // 绝了, 原来是这样加一个常数state = in;if (dfs(0, -100)) {string ans = "";for (int i = 0; i < limit; ++i) ans += dir[path[i]];return ans;}}return "unsolvable";
}int main()
{for (int i = 0; i < N2; ++i) {for (int j = 0; j < N2; ++j) {MDT[i][j] = abs(i / N - j / N) + abs(i % N - j % N);}}Puzzle in;for (int i = 0; i < N2; ++i) {cin >> in.f[i];if (in.f[i] == 0) {in.f[i] = N2;in.space = i;}}string ans = iterative_deepening(in);if (ans != "unsolvable") cout << ans.size() << endl;cout << ans << endl;
}

参考数据:

6 13 5 2
8 1 10 12
3 7 15 9
14 4 0 11 // 531 2 3 4
6 7 8 0
5 10 11 12
9 13 14 15  // 85 8 9 14
10 13 1 6
12 2 7 15
4 0 3 11  // 5612 7 2 4
5 1 0 9
14 13 6 8
3 15 10 11 // 475 11 10 7
13 0 9 3
14 2 4 8
1 15 6 12  // 385 1 4 7
2 0 11 3
9 6 10 8
13 14 15 12  // 149 14 13 15
5 3 11 6
8 12 2 1
10 7 4 0  // unsolvable

十六宫格随机数据: 排列置乱算法

#include <iostream>
#include <cstdlib>
#include <ctime>
#define RAND(l, r) l+(int)(r-l+1)*rand()/(RAND_MAX+1)
using namespace std;int main()
{srand(time(NULL));int data[16];for (int i = 0; i < 16; ++i) {data[i] = i;}for (int i = 15; i >= 0; --i) {int ind = RAND(0, i);swap(data[i], data[ind]);}for (int i = 0; i < 4; ++i) {for (int j = 0; j < 4; ++j) {cout << data[i*4 + j] << ' ';}cout << endl;}
}

IDA*算法解十六宫格拼图问题相关推荐

  1. 十六宫格拼图(A*/IDA*)(曼哈顿距离)

    传送门 迭代加深:通过单纯的深度优先搜索无法找出初始状态到最终状态的最短路径,但是重复进行限制最大深度的深度优先搜索(深度受限搜索)却可以.简单来说,就是在循环执行深度受限搜索的过程中逐步增加限制值l ...

  2. 汉(海)明码 | “十六宫格法” 破解汉(海)明码相关题目(附软考经典例题)

    文章目录 一.前言 二.奇偶校验码 三.海明码概念 四.十六宫格法 1.概述 2.原理 3.填写校验位 4.填写数据位 5.填写十六宫格首位 五.结语 一.前言 很多小伙伴在遇到"汉明码&q ...

  3. MFC Windows 程序设计[二十五]之五彩十六宫格(附源码)

    MFC Windows 程序设计[二十五]之五彩十六宫格 程序之美 前言 主体 运行效果 核心代码 逻辑分析 结束语 程序之美 前言 MFC是微软公司提供的一个类库(class libraries), ...

  4. MFC Windows 程序设计[三十五]之五彩十六宫格

    MFC Windows 程序设计[三十五]之五彩十六宫格 程序之美 前言 主体 运行效果 核心代码 逻辑分析 结束语 程序之美 前言 MFC是微软公司提供的一个类库(class libraries), ...

  5. 用H5实现四宫格切换九宫格,再切换十六宫格

    废话不多说,直接上代码 效果图如下 点击右上角按钮,即可切换4宫格,9宫格,16宫格 html部分 <!-- 切换按钮 --><div class="center" ...

  6. vue 1 2 3 4 6 9 16宫格 6宫格(六宫格) 9宫格(九宫格) 16宫格(十六宫格) 自定义宫格(样式篇)

    直接上图把,如果是你的菜,就点个赞,谢谢. 目录: 我直接在app.vue组件写了.. <template><div class="cell"><di ...

  7. Python拼图游戏源代码,可定制拼图图片,支持多种难度,可九宫格、十六宫格、二十五宫格

    配置环境 安装pygame模块 pip install pygame 引入资源 将照片,添加到resources/pictures路径下 照片.jpg格式 主函数代码 pintu.py 一个配置文件c ...

  8. 基于Canvas的N宫格拼图

    最近使用Canvas实现了一个N宫格拼图的游戏,感觉效果还是很不错的,不过我还是觉得九宫格就好了,太多了反而就复杂了.这里我就主要讲述九宫格的实现过程,其它的只是把数据结构扩大一下了. 实现效果 图片 ...

  9. 基于canvas+uniapp的9宫格拼图游戏组件

    基于 canvas+uniapp 的 9 宫格拼图游戏 涉及到的 canvas 基础知识 创建画布 <canvas id="'c1'"></canvas> ...

最新文章

  1. 重温目标检测--YOLO v1
  2. 最小割分治(最小割树):BZOJ2229 BZOJ4519
  3. sqlalchemy mysql_使用SQLAlchemy操作MySQL
  4. Ant Design Vue list表格组件
  5. 感想总结——热烈庆祝CSDN博客排名进入前20000名
  6. Day09: socket网络编程-OSI七层协议,tcp/udp套接字,tcp粘包问题,socketserver
  7. linux boot命令用法,Linux基础命令介绍 - 2
  8. 2020-09-08 Win7-Win10内部版本号
  9. swoole深入学习 2. tcp Server和tcp Client
  10. 计算机中submit commit区别
  11. 关于1stopt的补充说明
  12. python怎样分析文献综述怎么写_如何写文献综述?
  13. Scipy之图片降噪
  14. 【luogu1468】[Violet]蒲公英--求区间众数
  15. Hutool - 简化Java编程的法宝,让工作更高效
  16. 刚子扯谈:市场供需关系决定生存
  17. Memcached和Redis数据缓存系统
  18. 你不知道的 async、await 魔鬼细节
  19. 智能领域死伤无数,这家公司为什么能被谷歌亚马逊看重?
  20. Android 12.0 第三方无源码apk授予QUERY_ALL_PACKAGES等其他权限的方法

热门文章

  1. 深入剖析ARP地址欺骗病毒原理及欺骗过程
  2. 八航实业(深圳)有限公司离职感言 八航怎么样 八航公司工资待遇怎么样 八航公司货款
  3. 零食商城|基于springboot的零食商城
  4. 快应用、快服务、服务直达…这些到底是啥玩意
  5. Flutter ExpansionPanel 超级实用展开控件
  6. 数据挖掘情感分析python_数据挖掘实战:Twitter情感分析
  7. iTranslate for Mac(苹果专用翻译软件)
  8. Selenium模拟浏览器
  9. Azure 高性能计算介绍
  10. 利用正态分布进行异常点检测