1. A星算法解决修道士与野人问题
A星算法解决修道士与野人问题
1. 运行环境
- CPU:I5-10400
- 内存:16GB
- 系统:Win10 64位专业版,20H2
- IDE:Vistual Studio 2019专业版
2. 问题描述
假设有 n 个修道士和 n 个野人准备渡河,但只有一条能容纳 c 人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数(除非修道士个数为0)。如果两种人都会划船,试设计一个算法,确定他们能否渡过河去,若能,则给出一个完整的渡河方案。
3. 算法简介
3.1 A算法的基本原理分析
在或图的一般搜索算法中,如果在搜索过程的利用估价函数f(n)=g(n)+h(n)对open表中的节点进行排序,则该搜索算法为A算法。
- g(n):从初始节点到n的实际代价。因为n为当前节点,搜索已达到n点,所以g(n)可计算出。
- h(n):启发函数,从n到目标节点的最佳路径的估计代价。因为尚未找到解路径,所以h(n)仅仅是估计值。
对A算法中的g(n)和h(n)做出限制:
- g(n) >= g(n)(g*(n)为S0到n的最小费用)
- h(n) <= h*(n)(h*(n)为n到Sg的实际最小费用)
则算法被称为A*算法。
3.2 评估函数
评估函数:F = G + H
- F:搜索的总代价。
- G:开始状态到当前状态的代价。
- H:当前状态到结束状态的预估代价。
A星算法具有启发策略,在于其可以通过预估H值,降低走弯路的可能性,更容易找到一条更短的路径。其他不具有启发策略的算法,没有做预估处理,只是穷举所有可通过路径,然后从中挑选出一条最短的路径。
3.3 算法流程
- 将初始节点S0放入Open表中。
- 建立Close表,初始值为空。
- 如果Open表为空,则问题无解,退出。
- 从Open表中取出第一个节点n,并将n放入Close表中。
- 考察节点n是否是目标节点。如果是,则得到了问题的解,解的路径通过n到S0的指针获得,退出。
- 如果节点n不可拓展,则转到第2步。
- 扩展节点n,将子节点放入Open表中,并为每个子节点分配指向父节点的指针。
- 将Open表中的节点按照评估函数F值从小到大的顺序重新进行排序。
- 转向第3步。
4. 解决思路
4.1 状态空间表示方式
在这个问题中,需要考虑:
- 两岸的传修道士人数和野人人数。
- 船在左岸还是在右岸。
已知传教士和野人数:N(两者默认相同),船的最大容量:K。定义M:左岸传教士人数。C:左岸野人人数。B:左岸船个数。可用一个三元组来表示左岸状态,即S=(M, C, B)。将所有扩展的节点和原始节点存放在同一列表中。初始状态为(N, N, 1),目标状态为(0, 0, 0)。问题的求解转换为在状态空间中,找到一条从状态(N, N, 1)到状态(0, 0, 0)的最优路径。
4.2 评估函数
评估函数建立:f = d + h = d + M + C - 2*B。
- M:左岸修道士的人数。
- C:左岸野人的人数。
- B:取0或1。0表示船在右岸,1表示船在左岸。
- d:表示状态搜索深度,每次从当前状态搜索到下一状态时,下一状态在当前状态的深度基础上加1,初始状态的深度为0。
4.3 节点拓展
通过减少和增加修道士或也认得数量来拓展节点。当船在左岸时,向右岸移动,需要减少修道士和野人的数量;当船在右岸时,向左岸移动,需要增加修道士和野人的数量。
4.4 状态判断
- 左岸修道士人数等于总数或者为零:C>=0, C<=N。
- 左岸修道士人数在(0, N)之间时:C >=0 , M >= C , M <= N , C <= N , N-M >= N-C。
- 其他状态都不合法。
5. 关键代码
5.1 状态设计
定义结构体Node用来存储状态信息,包括左岸修道士和野人的人数,船在左岸还是右岸,当前状态的深度,当前状态到预估状态的预估代价,搜索总代价,指向下一结点的指针和指向父状态的指针。具体定义如下:
struct Node
{int m_M; // 左岸的修道士人数int m_C; // 左岸的野人人数int m_B; // 1:船在左岸;0:船在右岸int m_d; // 从开始状态到当前状态的代价,这里表示:结点深度int m_h; // 当前状态到结束状态的预估代价,h = M + C -2*B;int m_f; // 搜索总代价,f = d + h = d + M + C - 2*B;Node* next; // 下一节点Node* father; // 当前状态的父状态
public:Node();Node(int, int, int, int, Node*);~Node();
};
5.2 A*算法主要设计
起始状态为(N, N, 1), 每次从Open表中取出第一个结点,寻找可能的下一状态,并将当前结点加入Close表中。如果到达目标状态(0, 0, 0)时,搜索结束,输出生成结点数和搜索结点数。否则寻找当前结点的所有可能的下一状态。当整个Open表为空时,搜索结束。
while (m_Open->next != nullptr)
{Node* current = m_Open->next;m_Open->next = current->next;AddLinkList(m_Close, current);if (current->m_M == 0 && current->m_C == 0 && current->m_B == 0){std::cout << "搜索成功!生成节点:" << m_creatPoint << ",搜索节点:" << m_searchPoint << std::endl;m_endPoint = current;return true;}GetNext(current);
}
5.3 链表设计
为了便于存放生成的状态信息和已访问的状态信息,在MC类中设计了两个单链表,m_Open和m_Close。分别用于存放所有合法且没有访问的状态和所有已访问过的状态。
Node* m_Open;
Node* m_Close;
为了能够方便寻找Open表中搜索代价最小的状态,通过头插法,设计了一带头的升序单链表。在插入新结点时,如果到了链表尾部的,则直接在当前结点后面插入;否则与当前节点的下一结点比较,如果待插入结点的搜索代价小于当前结点下一结点的搜索代价,则将该结点插入当前结点后面,否则指链表的指针向下一结点移动。
void MC::AddLinkList(Node* LinkList, Node* node)
{Node* p = LinkList;while (p){if (p->next == nullptr){p->next = node;node->next = nullptr;return;}else if (node->m_f < p->next->m_f){node->next = p->next;p->next = node;return;}else p = p->next;}
}
5.4 输出设计
利用Node结点指向父结点进行递归倒序输出。
void MC::Output(Node* node)
{if (node == nullptr) return;if (node->father != nullptr) Output(node->father);std::cout << "(" << node->m_M << ", " << node->m_C << ", " << node->m_B << ")" << std::endl;
}
5.5 多线程设计
分配4个线程进行搜索,当某个线程搜索到结果时,while循环中的判断条件为真,搜索便完成。
bool MultiSearch()
{m_Open->next = new Node(m_M, m_C, 1, 0, nullptr);m_Open->next->next = nullptr;int N = 0;const int ThSize = 4;std::thread th[ThSize];for (int i = 0; i < ThSize; ++i) th[i] = std::thread(MultiGetNext);for (int i = 0; i < ThSize; ++i) th[i].detach();while (true) if (m_endPoint != nullptr) return true;
}
6. 运行结果与分析
C++20个结点单线程
C++20结点多线程
C++1000个结点单线程
C++1000个结点多线程
在多线程的程序中,分配了4个线程。从上图中不难发现,当搜索结点比较少时,多线程相较于单线程的运行优势体现的并不明显。这一方面是由于数据量比较小,运行时间都比较短;另一方面由于线程的创建和销毁都需要一定的时间,此外在多线程程序中,对共享数据需要互斥访问,对数据上锁和解锁的操作也耗费了一定的时间。当数据量比较大时,多线程的速度优势就体现出来了。
1. A星算法解决修道士与野人问题相关推荐
- 修道士和野人过河问题 A*算法 人工智能
/** * 2014-08-25 by Liy * 修道士和野人过河问题,一共有3个修道士和3个野人,1条船 * 1.船最多可乘坐2人 * 2.两岸边 野人的数量不能多于修道士的数量,否则修道士会被吃 ...
- Astar、A星算法解决八数码问题--python实现
一.问题描述 数码问题又称9宫问题,与游戏"华容道"类似.意在给定的3*3棋格的8个格子内分别放一个符号,符号之间互不相同,余下的一格为空格.并且通常把8个符号在棋格上的排列顺序称 ...
- 人工智能实践作业-修道士和野人过河问题
人工智能实践作业-修道士和野人过河问题: 用编程语言编写和调试一个基于深度优先搜索法的解决"野人与传教士过河"问题的程序.目的是学会运用知识表示方法和搜索策略求解一些考验智力的简单 ...
- 修道士与野人问题——C++源代码,伪代码,详细分析
前言:这一个经典的问题,可以把问题转换成数据结构中的 图 来解决.本博客节选自我去年7月份的数据结构报告 问题描述 假设有 n 个修道士和 n 个野人准备渡河,但只有一条能容纳 c 人的小船,为了防止 ...
- 修道士和野人java_野人与修道士问题——详细分析与C++源代码
前言:这一个经典的问题,可以把问题转换成数据结构中的 图 来解决.本博客节选自我去年7月份的数据结构报告 问题描述 假设有 n 个修道士和 n 个野人准备渡河,但只有一条能容纳 c 人的小船,为了防止 ...
- 修道士和野人java_修道士和野人问题
休闲时刻看看神经网络方面的书,发现了修道士和野人的问题,不禁勾引起我写算法的欲望,曾经的三只大老虎三只小老虎过河问题.人狼羊白菜过河问题.汉诺塔.哈夫曼等等各种算法瞬间在脑海中约隐约现,修道士和野人问 ...
- 修道士和野人问题:所有解、启发求解、简单界面
一.作业任务 修道士和野人问题:设有三个修道士和3个野人来到河边,打算用一条船从河的左岸渡到河的右岸去.但该船每次只能装载两个人,在任何岸边野人的数目都不得超过修道士的人数,否则修道士就会被野人吃掉. ...
- 2.修道士和野人问题
2.修道士和野人问题:设有三个修道士和3个野人来到河边,打算用一条船从河的左岸渡到河的右岸去.但该船每次只能装载两个人,在任何岸边野人的数目都不得超过修道士的人数,否则修道士就会被野人吃掉.假设野人服 ...
- 使用dfs求解修道士和野人问题
原文链接: 使用dfs求解修道士和野人问题 上一篇: js Set 的使用 下一篇: VMware 挂载U盘 1.问题描述 :这是一个古典问题.假设有n个道士和n个野人准备渡河.但只有一条能容纳c人的 ...
最新文章
- 构建RHEL上的extmail
- 附加到SQL2012的数据库就不能再附加到低于SQL2012的数据库版本
- struts2结果类型
- 你想知道的Spring框架配置使用流程和依赖注入!
- python正则匹配html标签_Python爬虫常用正则表达式及HTML网页标签分析总结
- 九宫格日记:微博难越,前路漫漫
- html页面打开前判断session,js判断session过期
- 安装增强功能时,未能加载虚拟光盘 D:\VirtualBox\VBoxGuestAdditions.iso到虚拟电脑
- 西门子CPU扩展以太网接口的一种解决方案
- C语言移动营业厅程序设计,【程序设计论文】C语言教学的移动应用程序设计(共2486字)...
- 全球及中国工业废物处置服务行业行业发展趋势及十四五规划展望报告2022-2028年
- 开灯问题 算法竞赛 (注释详细)
- python爬取appstore的评论数据的步骤_评论抓取:Python爬取微信在APPStore上的评论内容及星级...
- 银行笔试计算机基础知识点归纳,银行笔试:六大行笔试考情及重点梳理(内含免费模考)...
- windows10下Weblogic官网下载与安装教程
- 吴恩达 tensorflow2.0 实践系列课程(3):NLP
- PanDownload-百度网盘高速下载神级利器
- Gold的共价对接操作流程与Moe结合处理共价键的前处理流程
- 511遇见易语言基础命令组件和算法
- 国科大学习资料--模式识别与机器学习(黄庆明)--第二次随堂测答案(期末必看)
热门文章
- 2022年度电商“三巨头”阿里,京东,拼多多的“暗战”
- 驾校招生报名预约学车小程序开发制作
- m基于MATLAB的FM调制收音机仿真实现
- 十分钟快速DIY简易FM电台和收音机
- electron-builder打包见解
- 黑莓安全丑闻:加拿大警方2010年就能随意解密用户信息
- 无孔不入的Android侵入Windows系统
- 【CVPR2020】【逆推】Image Processing Using Multi-Code GAN Prior
- 阿里巴巴宣布入股B站 持股比例约8%
- OpenJudge NOI 1.5编程基础之循环控制(31-40题)C++ 解题思路