传教士与野人过河问题

代码模块参考文章:传教士与野人过河问题(numpy、pandas)_python过河问题_醉蕤的博客-CSDN博客

问题描述

一般的传教士和野人问题(Missionaries and  Cannibals):有N个传教士和C个野人来到河边准 备渡河。河岸有一条船,每次至多可供K人乘渡。 问传教士为了安全起见,应如何规划摆渡方案,使 得任何时刻,在河的两岸以及船上的野人数目总是 不超过传教士的数目,但允许在河的某一岸只有野 人而没有传教士。

这里讨论基于N=3,C=3,k=2

状态表示:(m,w,B);初始状态: (3, 3, 1);目标状态: (0,0,0)

启发性规则

操作符(产生式规则)
左岸往右岸运载产生式
L10  if(M,W,1) then (M-1,W,0)     (左往右运1传教士)
L01  if(M,W,1) then (M,W-1,0)     (左往右运1野人)
L11  if(M,W,1) then (M-1,W-1,0)  (左往右运1传教士和1野人)
L20  if(M,W,1) then (M-2,W,0)     (左往右运2传教士)
L02  if(M,W,1) then (M,W-2,0)     (左往右运2野人)

右岸往左岸运载产生式
R10  if(M,W,0) then (M+1,W,1)    (右往左运1传教士)
R01  if(M,W,0) then (M,W+1,1)    (右往左运1野人)
R11  if(M,W,0) then (M+1,W+1,1) (右往左运1传教士和1野人)
R20  if(M,W,0) then (M+2,W,1)    (右往左运2传教士)
R02  if(M,W,0) then (M,W+2,1)    (右往左运2野人)

路径方案展示

总共有四种解决方案

实现代码(含具体解释)

import numpy as np

M = int(input("请输入传教士的数量:"))  # 传教士数量
C = int(input("请输入野人的数量:"))  # 野人数量
K = int(input("请输入船的最大容量:"))  # 船的最大容量

child = []  # child:用于存储所有扩展节点
open_list = []  # open list
closed_list = []  # closed list


class State:
    def __init__(self, m, c, b):
        self.m = m  # 左岸传教士的数量
        self.c = c  # 左岸野人的数量
        self.b = b  # b为1时船在左岸;b为0时船在右岸
        self.g = 0  # 该结点所在的层级
        self.f = 0  # 该结点的启发性层度f = g + h
        # father这个属性是State
        self.father = None
        self.node = np.array([m, c, b])


init = State(M, C, 1)  # 初始节点
goal = State(0, 0, 0)  # 目标节点


# 判断状态是否合法
def safe(s):
    # 下面是要排除的条件,并且给出了为什么要排除的理由
    # s.m > M or s.m < 0 题目要求传教士的数量
    # s.c < 0 or (s.m != 0 and s.m < s.c) 有教士时,野兽的数量不能大于教士
    # s.m != M and M - s.m < C - s.c 对岸不全是怪兽时,对岸上的怪兽不能大于对岸上的教士
    if s.m > M or s.m < 0 or s.c > C or s.c < 0 or (s.m != 0 and s.m < s.c) or (s.m != M and M - s.m < C - s.c):
        return False
    else:
        # 状态合法
        return True


# 启发函数
def h(s):
    # 传教士数量+野人数量-船的位置与船的最大容量的乘积
    # 我们希望这个值越越小越好
    # 他的含义:岸上传教士数量+岸上野人数量之后
    # 如果这船在对岸(这是我们希望的,说明顺利渡河),于是我们就减上K*s.b
    # 如果船不在对岸,这不是我们希望的,所以不进行任何减数的操作(K=0)
    return s.m + s.c - K * s.b


# 判断两个状态是否相同
def equal(a, b):
    if np.array_equal(a.node, b.node):
        return 1, b
    else:
        return 0, b


# 判断当前状态是否与父状态一致
# new 是新结点,s是它的父节点
def back(new, s):
    if s.father is None:
        return False
    c = b = s.father
    while True:
        # 不断回溯,去找他的父结点是否与new结点相同
        a, c = equal(new, b)
        if a:
            return True
        b = c.father
        if b is None:
            # 此时找到的b是根结点,停止搜索
            return False


# 根据f值对open_list进行排序
def open_sort(l):
    l.sort(key=lambda x: x.f)


# 在open_list和closed_list中查找具有相同MCB属性的节点
def in_list(new, l):
    for item in l:
        if np.array_equal(new.node, item.node):
            return True, item
    return False, None


# A*算法
def A_star(s):
    A = []
    global open_list, closed_list
    open_list = [s]
    closed_list = []

    while True:  # open_list不为空
        for i in open_list:
            if np.array_equal(i.node, goal.node):  # 判断是否为目标节点
                A.append(i)
                open_list.remove(i)
        if not open_list:
            break
        get = open_list[0]
        open_list.remove(get)  # 从open_list中移除get节点
        closed_list.append(get)  # 将get节点加入closed_list

        # 获取get的子节点并考虑是否将其加入open_list
        for i in range(M + 1):  # 船上传教士数量
            for j in range(C + 1):  # 船上野人数量
                # 船上人数非法: 1:船上无人或者2:船上的人大于规定的载客数或者3:船上有教士并且野怪的数量大于教士
                if i + j == 0 or i + j > K or (i != 0 and i < j):
                    continue

                # 找到满足要求的相对于刚遍历完的结点get的下一个状态
                if get.b == 1:  # 当前船在左岸,下一个状态船在右岸
                    new = State(get.m - i, get.c - j, 0)
                    child.append(new)
                else:  # 当前船在右岸,下一个状态船在左岸
                    new = State(get.m + i, get.c + j, 1)
                    child.append(new)

                # safe(new)判断是否非法
                # back(new, get)这个相对于get结点的孩子回到父状态? TODO
                if not safe(new) or back(new, get):  # 状态非法或已经回到父状态
                    child.pop()
                else:
                    # 定义它的上一个状态的结点
                    new.father = get
                    # 孩子结点层级加1
                    new.g = get.g + 1
                    # 父亲的结点层级+父亲的启发性层度
                    new.f = get.g + h(get)
                    open_list.append(new)
                    open_sort(open_list)
    return A


# 递归打印路径
def print_path(f):
    if f is None:
        return
    # 先递归打印父结点的,再打印自己的结点
    print_path(f.father)
    print(f.node)
    # print(f.f)


if __name__ == '__main__':
    print('传教士数量:%d, 野人数量:%d, 船的最大容量:%d' % (M, C, K))
    final = A_star(init)
    print("共有%d种方案" % len(final))
    if final:
        c = 1
        for i in final:
            print('第%d个方案如下' % c)
            print_path(i)
            c += 1
    else:
        print('没有找到解决方案!')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/205904.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vscode集成git

1、首先电脑要安装git 打开git官网地址&#xff1a;Git进行下载&#xff0c;如下图界面&#xff1a; 如图片中描述&#xff1a;一般进入官网后会识别电脑对应系统&#xff08;识别出了我的电脑是Windows系统 。如果未识别到电脑系统&#xff0c;可在左侧选择自己电脑对应的系统…

Maven——使用Nexus创建私服

私服不是Maven的核心概念&#xff0c;它仅仅是一种衍生出来的特殊的Maven仓库。通过建立自己的私服&#xff0c;就可以降低中央仓库负荷、节省外网带宽、加速Maven构建、自己部署构件等&#xff0c;从而高效地使用Maven。 有三种专门的Maven仓库管理软件可以用来帮助大家建立…

vue3使用动态component

使用场景&#xff1a; 多个组件通过component标签挂载在同一个组件中&#xff0c;通过触发时间进行动态切换。vue3与vue2用法不一样&#xff0c;这里有坑&#xff01; 使用方法&#xff1a; 1.通过vue的defineAsyncComponent实现挂载组件 2.component中的is属性 父组件&am…

deque容器结构学习笔记

1.结构图 2.deque对比vector和list deque双端队列&#xff0c;就像是list和vector的结合 vector&#xff1a; 优点&#xff1a;1.可以随机读取 2. 空间利用率高 缺点&#xff1a;1. 除了尾插尾删&#xff0c;其他插入删除效率比较低 2. 扩容效率低 list&#xff1a; 优点&…

第16关 革新云计算:如何利用弹性容器与托管K8S实现极速服务POD扩缩容

------> 课程视频同步分享在今日头条和B站 天下武功&#xff0c;唯快不破&#xff01; 大家好&#xff0c;我是博哥爱运维。这节课给大家讲下云平台的弹性容器实例怎么结合其托管K8S&#xff0c;使用混合服务架构&#xff0c;带来极致扩缩容快感。 下面是全球主流云平台弹…

threeJs引入模型使用3D模型(vite+React+Ts)

要在 Three.js 中使用 3D 模型&#xff0c;你需要加载模型文件并将其添加到场景中。Three.js 支持多种不同的模型格式&#xff0c;比如 OBJ、FBX、GLTF 等。 init vitelatest //创建一个vite的脚手架 选择react并配置Ts 安装three.js准备 npm install react-three/drei np…

Ubuntu Server 20.04.6下Anaconda3安装Pytorch

环境 Ubuntu 20.04.6 LTS Anaconda3-2023.09-0-Linux-x86_64.sh conda 23.7.4 Pytorch 1.11.0 安装 先创建一个工作环境&#xff0c;环境名叫lia&#xff1a; conda create -n lia python3.8环境的使用方法如下&#xff1a; conda activate lia # 激活环境 conda deactiv…

2021年8月18日 Go生态洞察:整合Go的网络体验

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

ps 透明印章制作

ps 透明印章制作 1、打开不透明印章2、抠出红色印章3、新建图层4、填充红色印章到新图层5、导出透明印章 1、打开不透明印章 打开ps软件&#xff0c;菜单栏选择 文件-打开 选择本地不透明印章 打开 2、抠出红色印章 ps菜单栏 选择 选择-色彩范围 点击色彩范围 色彩范围窗口 取…

13.单调栈(接雨水、柱状图最大矩形)【灵神基础精讲】

单调栈【灵神基础精讲】 https://www.bilibili.com/video/BV1VN411J7S7/ 单调栈和单调队列的关系&#xff1a;单调队列单调栈滑窗 单调栈&#xff0c;顾名思义就是栈内元素单调按照递增(递减)顺序排列的栈。 适用问题&#xff1a;单调栈分为单调递增栈和单调递减栈&#xff0c…

在Linux上安装KVM虚拟机

一、搭建KVM环境 KVM&#xff08;Kernel-based Virtual Machine&#xff09;是一个基于内核的系统虚拟化模块&#xff0c;从Linux内核版本2.6.20开始&#xff0c;各大Linux发行版就已经将其集成于发行版中。KVM与Xen等虚拟化相比&#xff0c;需要硬件支持的完全虚拟化。KVM由内…

Nginx实现(动静分离)

动静分离应该是听的次数较多的性能优化方案&#xff0c;那先思考一个问题&#xff1a;「「为什么需要做动静分离呢&#xff1f;它带来的好处是什么&#xff1f;」」 其实这个问题也并不难回答&#xff0c;当你搞懂了网站的本质后&#xff0c;自然就理解了动静分离的重要性。先来…

设计模式之装饰模式(2)--有意思的想法

目录 背景概述概念角色 基本代码分析❀❀花样重难点聚合关系认贼作父和认孙做父客户端的优化及好处继承到设计模式的演变过程 总结 背景 这是我第二次写装饰模式&#xff0c;这一次是在上一次的基础上进一步探究装饰模式&#xff0c;这一次有了很多新的感受和想法&#xff0c;也…

如何提高销售技巧,增加客户的成交率?

如何提高销售技巧&#xff0c;增加客户的成交率&#xff1f; 在如今的市场环境中&#xff0c;销售技巧的高低往往决定了你是否能够成功地打动客户的心。想要提高销售业绩&#xff0c;除了产品质量和服务的保障&#xff0c;更需要你精进销售技巧&#xff0c;从而让客户愿意为你…

MySQL三大日志详细总结(redo log undo log binlog)

MySQL日志 包括事务日志&#xff08;redolog undolog&#xff09;慢查询日志&#xff0c;通用查询日志&#xff0c;二进制日志&#xff08;binlog&#xff09; 最为重要的就是binlog&#xff08;归档日志&#xff09;事务日志redolog&#xff08;重做日志&#xff09;undolog…

一个资深测试工程师面试一来就问我这些题目

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

一个数据中心的PUE修养,必将迎来液冷存储的曙光

实现小于1.3的PUE硬指标&#xff0c;数据中心液冷存储将功不可没。 【全球存储观察 &#xff5c; 科技热点关注】 4000亿千瓦时&#xff0c;能耗如此惊人&#xff0c;这是预计到2030年全国数据中心的年耗电总量。 小于1.3&#xff0c;看似微不足道的数字&#xff0c;这是新建…

有趣的代码——井字棋游戏的实现

前面我们讲解过一个猜数字游戏的实现&#xff0c;想来应该让大家感受到了属于编程的趣味性&#xff0c;并且在实现过程中应该也收获了知识。但猜数字这种简单的游戏肯定满足不了大家对于游戏的高标注、严要求&#xff0c;估计玩不了多久就会没有兴趣了&#xff0c;所以&#xf…

8. 队列

队列(queue)是一种遵循先入先出规则的线性数据结构。顾名思义&#xff0c;队列模拟了排队现象&#xff0c;即新来的人不断加入队列的尾部&#xff0c;而位于队列头部的人逐个离开。 如下图所示&#xff0c;我们将队列的头部称为“队首”&#xff0c;尾部称为“队尾”&#xff…

基于Web邮箱的邮件系统

题目: 基于web的邮件收发系统设计与实现 摘 要 计算机的应用已经越来越广泛&#xff0c;它从产生到完善已经差不多有50年左右的历史&#xff0c;更新换代速度非常快&#xff0c;在人们生活、工作中都发挥了不可替代的作用&#xff0c;几乎所有行业都离不开它&#xff0c;已经成…
最新文章