传教士与食人者问题-暴力搜索法(python)

本文介绍了如何使用Python实现最简单的遍历搜索算法解决经典的传教士与食人者问题,确保在渡河过程中任何时候传教士数量都不少于食人者。通过生成可行状态、可行转移,然后进行暴力搜索找到最优的渡河路径。在3个传教士和3个食人者的例子中,发现存在4种最优转移方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

前言

一、问题描述

二、主要函数

1.生成可行状态

2.生成可行转移

3.寻找最优转移路径

三、测试运行

1.生成可行状态

2.暴力搜索

总结


前言

        传教士(missionaries)与食人者(cannibals)问题是出处于第一篇从分析角度处理问题表述的论文,故在人工智能中很有名。本文基于python,使用最简单的遍历搜索算法来寻找最优渡河方案,希望对读者有所启发。


一、问题描述

         若干传教士和食人者都在河的一岸(以下称为原岸),有一条能载1-2个人的船。请设法使所有人都渡河到河的对岸,并要求在任何地方都不能出现食人者多于传教士的情况。

二、主要函数

1.生成可行状态

def States(ms_0,cs_0):# ms_0,cs_0 分别为初始传教士和食人者的数量
    states = np.array([]).astype(int)# 记录可行的状态
    n = 0 # 记录可行状态个数
    for i in [0,1]:# 船在原岸为 0 ,在对岸为 1 
        for ms in range(ms_0+1):
            if (ms==1) or (ms==2):# 保证有传教士在的任何地方,其人数都不少于食人者的人数
                cs = ms
                states = np.append(states,[i,ms,cs])
                n+=1
            else:
                for cs in range(cs_0+1):
                    states = np.append(states,[i,ms,cs])
                    n+=1
    states = np.reshape(states,(n,3))
    print("可行的状态有%d个"%n)
    print("分别是:\n",states)
    return n,states
'''部分状态可能不会存在,但不影响计算转移过程,故未删去'''

2.生成可行转移

def Actions(m):# m 为船的最大载客量
    actions = np.array([]).astype(int)# 记录可行的转移
    p = 0 # 记录可行转移个数
    for ms in range(m+1):
        for cs in range(m+1):
            if (ms+cs)>=1 and (ms+cs) <= m :
                actions = np.append(actions,[1,ms,cs])# 船的位置在这里设为 1 ,可方便后续计算
                p+=1
                        
    actions = np.reshape(actions,(p,3))
    print("可行的转移有%d个"%p)
    print("分别是:\n",actions)
    return p,actions

3.寻找最优转移路径

def trans_path(state_0,state_z,states,actions):
# 初态,终态,可行状态,可行转移
    l = 1 # 已出现的状态有多少个,而状态转移次数为 l-1
    state_s = np.array([[state_0]])# 转移路径
    state_1 = state_0
    action_s = actions.copy()# 建立副本,防破坏输入
    
    while any(state_1 != state_z) and action_s.shape[0] > 0:
        
        i = state_1[0]# 1 表示在原岸,0 表示在对岸
        fit = False
        if i == 1:
            while action_s.shape[0] > 0:# 无放回随机取样
                row = np.random.choice(action_s.shape[0])
                action = action_s[row]

                state_2 = state_1 - action
                if all(state_2>=0):# 验证状态是否符合规则
                    for j in range(states.shape[0]):
                        if all(state_2 == states[j]):
                            fit = True
                            break
                    if fit:# 符合则跳出循环
                        break

                action_s = np.delete(action_s,row,0)
        else:
            while action_s.shape[0] > 0:
                row = np.random.choice(action_s.shape[0])
                action = action_s[row]

                state_2 = state_1 + action
                if all(state_2<=3):
                    no_repeat = True
                    for j in range(states.shape[0]):
                        if all(state_2 == states[j]):
                            fit = True
                            break
                    if fit:
                        break
                action_s = np.delete(action_s,row,0)


        if action_s.shape[0] > 0:# 转换到下一状态
            l+=1
            state_s = np.append(state_s,state_2)
            states = np.delete(states,j,0) # 移除已出现状态,避免重复
            state_1 = state_2
            action_s = actions.copy()
            
            
    if action_s.shape[0] > 0:
        state_s = np.reshape(state_s,(l,3))
        return l,state_s
    else: # 此处只是为了防止后续报错,可保证了运行该函数后有有返回值
        return 20,state_s 

三、测试运行

        我们以3个传教士和3个食人者进行举例测试

1.生成可行状态

import numpy as np
ms_0,cs_0 = 3,3# 初始传教士和食人者的数量
state_0 = np.array([1,ms_0,cs_0])# 初态
state_z = np.array([0,0,0])# 终态
n,states = States(ms_0,cs_0)
m = 2 # 船最大承载人数
p,actions = Actions(m)

结果如下:

2.暴力搜索

L = np.array([])
State_s = np.array([])
n = 0 # 记录最短转移路径的所有可能次数

for i in range(100):# 循环100次,以找出最短状态转移
    l,state = trans_path(state_0,state_z,states,actions)
    L = np.append(L,l)
    
repeat = np.zeros(int(min(L)))
for i in range(100):# 循环100次,以找出所有最短状态转移
    l,state = trans_path(state_0,state_z,states,actions)
    if l == min(L):
        for j in range(n):
            repeat = np.zeros(l)
            for k in range(l):
                if all(state[k] == State_s[j][k]):
                    repeat[k] = True
            if repeat.all():
                break
        if not repeat.all():
            n+=1
            State_s = np.append(State_s,state)
            State_s = np.reshape(State_s,(n,l,3))
            
print("共有%d种转移方法。"%n)
print("状态转移过程分别是:\n",State_s)

结果如下:


总结

        我们可以发现在本例中,对于最优转移步骤,除了第一步状态转移和倒数第二步状态转移有两种选择以外,其他状态转移过程都有且仅有一种状态,故共有4种最优转移方案。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值