傳教士過河問題python實現所有方法列印

2021-10-06 02:44:43 字數 4861 閱讀 2791

有 n 個傳教士和 n 個野人來到河邊渡河,河岸有一條船,每次至多可供 k 人乘渡。問:傳教士為了安全起見,應如何規劃擺渡方案,使得任何時刻, 河兩岸以及船上的野人數目總是不超過傳教士的數目(否則不安全,傳教士有可能被野人吃掉)。 即求解傳教士和野人從左岸全部擺渡到右岸的過程中,任何時刻滿足 m (傳教土數) ≥ c 野人數)和 m+c≤k 的擺渡方案。

傳教士與野人過河問題是人工智慧裡面非常經典的演算法題,曾經是2023年360公司的面試題,因此網上有各種各樣的解決思路和**設計,但是我發現網上的演算法思路非常好,**設計的儘管多種多樣,但都不滿足我今天這個要做的這個專案需求,大部分都是規定好了3個傳教士和3個野人,船上最多只能做2人,或者是先按照3人舉例,舉例完就沒了。這樣的限制是不符合今天這種需求的,因為今天的問題描述中並沒有說明會有幾個傳教士和野人,只能保證傳教士和野人一般多,也沒有說明一條船最多做幾個人,因此網上大部分**案例不合題。再有一種就是允許我們輸入有多少人和船最多坐多少人,但是他沒有找出所有的方案。所以今天就自己動手做一下這個演算法。

假設以傳教士和野人的數量n都為3,船一次最大的載人量k為2人。

初始狀態:河左岸有3個野人、3個傳教士;河右岸有0個野人和0個傳教士;船停在左岸,船上有0個人。初始狀態(3,3,1)

目標狀態:河左岸有0個野人和0個傳教士;河右岸有3個野人和3個傳教士;船停在右岸,船上有0個人。目標狀態(0,0,0)

將整個問題抽象成怎樣從初始狀態經一系列的中間狀態從而達到目標狀態,狀態的改變是通過划船渡河來引發的。

根據要求,共得出以下5中可能的渡河方案:

(1)渡2傳教士

(2)渡2野人

(3)渡1野人1傳教士

(4)渡1傳教士

(5)渡1野人

本程式要定義狀態結點,使用集合儲存狀態結點,使用遞迴的思想來尋找目標狀態。

設 m 為傳教士總人數,c 為野人總人數,k 為每條船乘坐人數,船的狀態表示為 b。

在 m=3,c=3,k=2 情況下:

令 m 表示在左岸的傳教士人數,c 表示在左岸的野人數,b=1 表示船在左岸, b=0 表示船在右岸。

因此可用(m, c, b)表述當前狀態。

則,初始狀態為(3, 3, 1), 目標狀態為(0, 0, 0)。

渡河時,為確保安全,需滿足:

0 ≤ m ≤ 3,0 ≤ c ≤ 3, b ∈ , m > c(m 不為 0 時), 3-m > 3-c(m 不為 3 時)

啟發函式:h=m+c-kb

# -*- coding: utf-8 -*-

import operator

__metaclass__ = type

m = int(input("請輸入傳教士的人數:")) # 傳教士

c = int(input("請輸入野人的人數:")) # 野人

k = int(input("請輸入船的最大容量:"))

# 每船乘坐人數

child = # child用來存所有的拓展節點

open_list = # open表

closed_list = # closed表

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

self.father = none

self.node = [m, c, b]

init = state(m, c, 1) # 初始節點

goal = state(0, 0, 0) # 目標

#0 ≤ m ≤ 3,0 ≤ c ≤ 3, b ∈ , 左岸m > c(m 不為 0 時), 右岸3-m > 3-c(m 不為 3 時)

def safe(s):

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):

return s.m + s.c - k * s.b

# return m - s.m + c - s.c

def equal(a, b):

if a.node == b.node:

return 1,b

else:

return 0,b

# 判斷當前狀態與父狀態是否一致

def back(new, s):

if s.father is none:

return false

#判斷當前狀態與祖先狀態是否一致

c=b=s.father

while(1):

a,c=equal(new, b)

if a:

return true

b=c.father

if b is none:

return false

# 將open_list以f值進行排序

def open_sort(l):

the_key = operator.attrgetter('f') # 指定屬性排序的key

l.sort(key=the_key)

# 擴充套件節點時在open表和closed表中找原來是否存在相同mcb屬性的節點

def in_list(new, l):

for item in l:

if new.node == item.node:

return true, item

return false, none

def a_star(s):

a=global open_list, closed_list

open_list = [s]

closed_list =

#print(len(open_list))

# print ('closed list:') # 選擇列印open表或closed表變化過程

#print(s.node)

#a=1

while(1): # open表非空

#get = open_list[0] # 取出open表第乙個元素get

for i in open_list:

if i.node == goal.node: # 判斷是否為目標節點

open_list.remove(i)

if not(open_list):

break

get=open_list[0]

open_list.remove(get) # 將get從open表移出

# 以下得到乙個get的新子節點new並考慮是否放入openlist

for i in range(m+1): # 上船傳教士

for j in range(c+1): # 上船野人

# 船上非法情況

if i + j == 0 or i + j > k or (i != 0 and i < j):

continue

#a=a+1

if get.b == 1: # 當前船在左岸,下一狀態統計船在右岸的情況

new = state(get.m - i, get.c - j, 0)

#print(1)

else: # 當前船在右岸,下一狀態統計船在左岸的情況

new = state(get.m + i, get.c + j, 1)

#print(2)

#優先順序:not>and>ture。如果狀態不安全或者要拓展的節點與當前節點的父節點狀態一致。

if not safe(new) or back(new, get): # 狀態非法或new折返了

child.pop()

#如果要拓展的節點滿足以上情況,將它的父親設為當前節點,計算f,並對open_list排序

else:

new.father = get

new.g = get.g + 1 #與起點的距離

new.f = get.g + h(get) # f = g + h

#print(len(open_list))

open_sort(open_list)

# 列印open表或closed表

#for o in open_list:

# for o in closed_list:

#print(o)

#print(o.node)

# print(o.father)

#print(a)

return(a)

# 遞迴列印路徑

def printpath(f):

if f is none:

return

printpath(f.father)

#注意print()語句放在遞迴呼叫前和遞迴呼叫後的區別。放在後實現了倒敘輸出

print(f.node )

if __name__ == '__main__':

print ('有%d個傳教士,%d個野人,船容量:%d' % (m, c, k))

final = a_star(init)

print("有{}種方案".format(len(final)))

if final:

for i in(final):

print ('有解,解為:')

printpath(i)

else:

print ('無解!')

傳教士過河問題

傳教士過河問題是乙個耳熟能詳的問題,也是很多智力遊戲中經常出的題目,但是,我從來都沒有學到乙個解決這類問題的通解。在 人工智慧 課程中學到了,當時覺得這個題目很有意思,作為大四的學生,本來已經對上課沒有什麼興趣了。不過,恰巧,我去上的那幾節課中有這個問題。但是,老師的課件上好像有些問題,我課下想了想...

傳教士野人過河問題

這個問題是人工智慧中經典的搜尋問題,下面用深度優先搜尋演算法來解這個題,示例 如下 include include include using namespace std typedef struct mcnode listfringe 相當於佇列 vectorclosed closed表 判斷是否...

野人與傳教士過河問題

題目 設有三個 傳教士和3個野人來到河邊,打算乘乙隻船從右岸渡到左岸去。該船的負載能力為兩個人。在任何時候,如果野人人數超過傳教士人數那麼野人就會把傳教士吃掉。他們怎樣才能用這條船安全地把所有人都渡過河去?河岸 a 對岸 b 船上 2c 2y 1c 1y回去 傳教士划船回去 河岸 a 對岸 b 船上...