人工智能课程的实验(我的解法其实更像是算法课程的实验)

用到的算法:深度优先搜索、宽度优先搜索(状态扩展的不同策略)

数据结构:表示状态的结构体、多维数组

(可能是最近做算法竞赛题的影响,这次并不像以前那样依赖类和面向对象了,而是用最简单(几乎没有封装)的数据表示方法和大量的全局变量来存储数据,用面向过程的写法,以快速解决某一问题为目的设计程序。安全性和可扩展性势必降低,有些技巧的使用也让代码变得难懂;但是代码简洁,节省运行的时间和空间开销,这应该就是算法竞赛更加看重的吧)

这次用了C++写了控制台版,打印状态三元组;又用C#写了图形界面版,打印出圆盘的状态。

二者的算法是完全一致的,执行结果也一致,只是语法和输出的一些处理不同。

下面是运行结果截图,以深度优先为例

 

下面以C++版为例介绍实现

结构体和全局变量的定义

 struct State
{
int a, b, c;//表示圆盘朝南方向的数字
State(){}
State(int na, int nb, int nc) :a(na), b(nb), c(nc){}
}; int vis[][][];//用来记录每个状态是否被访问过
int num;//用来记录第几个状态

DFS(用栈实现)

 int dfs()
{
stack<State> sta;
sta.push(State(,,));
vis[][][] = ;
while (sta.size())
{
State cur = sta.top();
sta.pop();
cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c<<")"<<endl;
if (cur.a == && cur.b == && cur.c == )
return ;//命中
if (cur.c - > && vis[cur.a][cur.b][cur.c-] == )
{
vis[cur.a][cur.b][cur.c-] = ;
sta.push(State(cur.a, cur.b, cur.c-));
}
if (cur.b - > && vis[cur.a][cur.b-][cur.c] == )
{
vis[cur.a][cur.b-][cur.c] = ;
sta.push(State(cur.a, cur.b-, cur.c));
}
if (cur.a - > && vis[cur.a-][cur.b][cur.c] == )
{
vis[cur.a-][cur.b][cur.c] = ;
sta.push(State(cur.a-, cur.b, cur.c));
}
}
return ;
}

BFS(用队列实现)

 int bfs()
{
queue<State> que;
que.push(State(, , ));
vis[][][] = ;
while (que.size())
{
State cur = que.front();
que.pop(); cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c<<")"<<endl;
if (cur.a == && cur.b == && cur.c == )
return ;//命中
if (cur.a - > && vis[cur.a - ][cur.b][cur.c] == )
{
vis[cur.a-][cur.b][cur.c] = ;
que.push(State(cur.a-, cur.b, cur.c));
}
if (cur.b - > && vis[cur.a][cur.b - ][cur.c] == )
{
vis[cur.a][cur.b-][cur.c] = ;
que.push(State(cur.a, cur.b-, cur.c));
}
if (cur.c - > && vis[cur.a][cur.b][cur.c - ] == )
{
vis[cur.a][cur.b][cur.c-] = ;
que.push(State(cur.a, cur.b, cur.c-));
}
}
return ;
}

  你会发现DFS和BFS的不同仅在于对某一状态(节点)进行扩展时,其兄弟结点被暂存的顺序。这个顺序决定了节点扩展的顺序,即将“树”这样的“半线性结构”转化为“线性结构”的不同策略。

  因为这两种搜索策略都是从一开始就知道要对每一个未命中的节点进行怎样的处理,即搜索过程中产生的结果对搜索策略没有影响,所以这两种都属于“盲目式搜索”。相对而言,人工智能领域发展出“启发式搜索”,即在进行待扩展节点的选取时,要根据一个“估价函数”来选取“最有希望”的节点分支进行下一步扩展。

老师的习题解析中对DFS进行了改进,在扩展某个节点时判断的是它的子节点,这样可以尽早找到目标。个人认为这相当于在局部做了广度优先。

改进后的访问过的状态由17个降到5个,但其实加上判断过又未命中的兄弟节点,是降到了11个。如下

改进版DFS的代码

 int dfs_a()
{
stack<State> sta;
sta.push(State(,,));
vis[][][] = ;
while (sta.size())
{
State cur = sta.top();
sta.pop();
cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c<<")"<<endl; if (cur.c - > && vis[cur.a][cur.b][cur.c-] == )
{
vis[cur.a][cur.b][cur.c-] = ;
if (cur.a == && cur.b == && cur.c- == )
{
cout << ++num << ". ("<<cur.a<<","<<cur.b<<","<<cur.c-<<")"<<endl;
return ;//命中
}
sta.push(State(cur.a, cur.b, cur.c-));
}
if (cur.b - > && vis[cur.a][cur.b-][cur.c] == )
{
vis[cur.a][cur.b-][cur.c] = ;
if (cur.a == && cur.b- == && cur.c == )
{
cout << ++num << ". ("<<cur.a<<","<<cur.b-<<","<<cur.c<<")"<<endl;
return ;//命中
}
sta.push(State(cur.a, cur.b-, cur.c));
}
if (cur.a - > && vis[cur.a-][cur.b][cur.c] == )
{
vis[cur.a-][cur.b][cur.c] = ;
if (cur.a- == && cur.b == && cur.c == )
{
cout << ++num << ". ("<<cur.a-<<","<<cur.b<<","<<cur.c<<")"<<endl;
return ;//命中
}
sta.push(State(cur.a-, cur.b, cur.c));
}
}
return ;
}

以上就是算法的内容。

关于C#图形界面的实现,也很简单。下面是显示每个圆盘状态的函数

 //打印状态函数
private void print_circle(State st,int num)
{
Thread.Sleep();
Graphics gra = this.groupBox1.Controls[num-].CreateGraphics();//指定第num个圆盘显示 gra.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; Pen pen = new Pen(Color.Black);//画笔颜色 gra.DrawEllipse(pen, , , , );//画圆的方法,x坐标、y坐标、宽、高
gra.DrawEllipse(pen, , , , );
gra.DrawEllipse(pen, , , , ); Font font1 = new Font("", , FontStyle.Bold);
Font font2 = new Font("", , FontStyle.Bold);
Brush bush = new SolidBrush(Color.Red);//字填充的颜色
gra.DrawString(num + "", font2, bush, , );
gra.DrawString("A", font1, bush, , );
gra.DrawString("B", font1, bush, , );
gra.DrawString("C", font1, bush, , );
gra.DrawString(st.a+"", font1, bush, , );
gra.DrawString(st.b+"", font1, bush, , );
gra.DrawString(st.c+"", font1, bush, , );
}

关于界面的设计,预先放好足够多的picturebox,将它们放入一个groupbox里,然后根据传递进来的参数找特定的picturebox画图。

  在遍历groupbox时,我发现里面元素的顺序如果不是按顺序拖入的,会非常错乱。。。由于很菜鸟,我百度了很久都没有找到直接修改内部顺序的方法,只好打开groupbox的定义代码,调整添加元素函数的顺序才得以重排。

  以上是我对这个圆盘问题的求解。

  在检查实验时,老师说这个问题更通用的抽象是一个5个参数的“搜索机”,5个参数分别为:初始状态集合、目标状态集合、初始状态、目标状态、状态转移函数。(百度后发现好像图灵机的模型)这种方法需要在一开始生成所有可能状态。相比我的“动态生成状态”策略,这样势必会增加空间开销,但在搜索次数多时可以节省重复生成状态的时间,两种方法各有千秋吧。后一种方法更切合现在学习人工智能课的“状态空间搜索”思想。

用BFS和DFS解决圆盘状态搜索问题的更多相关文章

  1. BFS 、DFS 解决迷宫入门问题

    问题 B: 逃离迷宫二 时间限制: 1 Sec  内存限制: 128 MB提交: 12  解决: 5[提交][状态][讨论版] 题目描述 王子深爱着公主.但是一天,公主被妖怪抓走了,并且被关到了迷宫. ...

  2. 用深度优先搜索(DFS)解决多数图论问题

    前言 本文大概是作者对图论大部分内容的分析和总结吧,\(\text{OI}\)和语文能力有限,且部分说明和推导可能有错误和不足,希望能指出. 创作本文是为了提供彼此学习交流的机会,也算是作者在忙碌的中 ...

  3. 深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现

    1.基础部分 在图中实现最基本的操作之一就是搜索从一个指定顶点可以到达哪些顶点,比如从武汉出发的高铁可以到达哪些城市,一些城市可以直达,一些城市不能直达.现在有一份全国高铁模拟图,要从某个城市(顶点) ...

  4. 搜索分析(DFS、BFS、递归、记忆化搜索)

    搜索分析(DFS.BFS.递归.记忆化搜索) 1.线性查找 在数组a[]={0,1,2,3,4,5,6,7,8,9,10}中查找1这个元素. (1)普通搜索方法,一个循环从0到10搜索,这里略. (2 ...

  5. 搜索(BFS、DFS、回溯)

    这类题是最简单的了都是一个套路,不像动态规划一类题一个套路,没做过就是不会也极难想出来. 一.BFS 解决的问题:用来初始点解决到指定点的最短路径问题,因为图的每一层上的点到初始点的距离相同.(注意是 ...

  6. bfs和dfs辨析—基础复习(从stack和queue的角度来理解区别,加深理解,不再模糊)

    参考: https://www.cnblogs.com/Tovi/articles/6194815.html https://blog.csdn.net/dangzhangjing97/article ...

  7. BFS和DFS详解

    BFS和DFS详解以及java实现 前言 图在算法世界中的重要地位是不言而喻的,曾经看到一篇Google的工程师写的一篇<Get that job at Google!>文章中说到面试官问 ...

  8. 通俗理解BFS和DFS,附基本模板

    1.BFS(宽度优先搜索):使用队列来保存未被检测的节点,按照宽度优先的顺序被访问和进出队列 打个比方:(1)类似于树的按层次遍历 (2)你的眼镜掉在了地上,你趴在地上,你总是先摸离你最近的地方,如果 ...

  9. ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 A、Saving Tang Monk II 【状态搜索】

    任意门:http://hihocoder.com/problemset/problem/1828 Saving Tang Monk II 时间限制:1000ms 单点时限:1000ms 内存限制:25 ...

随机推荐

  1. double精度的坑与BigDecimal

    近期经常接触支付相关的功能,在开发及测试过程中,开始金额都使用的是double类型,而近期新进的需求存在支付时打折的情况,也就是会出现如 1.23元的情况,那么这时候问题来了,如果是直接使用1.23进 ...

  2. iOS 捕获系统外异常

    iOS 捕获系统外异常 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太 ...

  3. android GestureDetector 手势的判断

    import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.Ges ...

  4. Hadoop-2.2.0中文文档—— Common - CLI MiniCluster

    目的 使用 CLI MiniCluster, 用户能够简单地仅仅用一个命令就启动或关闭一个单一节点的Hadoop集群,不须要设置不论什么环境变量或管理配置文件. CLI MiniCluster 同一时 ...

  5. mysql求时间差

    SELECT TIMESTAMPDIFF(SECOND, now(), "2012-11-11 00:00:00") 语法为:TIMESTAMPDIFF(unit,datetime ...

  6. html中编写js的方式

    第一种:引用外部的js文件 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http ...

  7. ubuntu14.04 install flow.

    打开虚拟机,点击菜单上的“文件”,选择新建虚拟机,如下图所示: 注释:这里选择自定义安装,点击下一步. 这里我的虚拟机版本最新是10的,就选最新的,然后点击下一步,如下图: 这里选择要安装的Ubunt ...

  8. Python:常见错误集锦(持续更新ing)

    初学Python,很容易与各种错误不断的遭遇.通过集锦,可以快速的找到错误的原因和解决方法. 1.IndentationError:expected an indented block 说明此处需要缩 ...

  9. Swift中的便利构造器和构造器链

    import UIKit // 1.一个类中至少有一个指定构造器, 其必须负责初始化类中所有的实例存储属性 // 2.便利构造器属于次要的, 辅助性的构造器 // 3.类中可以不定义便利构造器, 便利 ...

  10. C#类、静态类

    C#是面向对象(object-oriented)类型的计算机语言,使用类(class)来体现面向对象的概念. 分类(classification) 我们在现实世界中,会遇到很多被分类的事物.例如,动物 ...