python农夫过河问题( Dijkstra算法实现)

1问题描述

1.1假设

农夫需要把狼、羊、菜和自己运到河对岸去。

只有农夫能够划船,而且船比较小,除农夫之外每次只能运一种东西。

还有一个棘手问题,就是如果没有农夫看着,羊会偷吃菜,狼会吃羊。

 

 

1.2决策目标

让农夫能够安全地安排这些东西和他自己过河。

找到⼀个能够将农夫、狼、⽺、菜安全地从左岸移动到右岸的最短路径。

1.3变量

农夫、狼、⽺、菜的位置(0: 左岸, 1: 右岸)

2状态构造

2.1状态表示

使⽤四元组 (farmer, wolf, goat, cabbage)来表⽰状态,每个元素为0 或1,表⽰相应物品的位置(0: 左 岸,1: 右岸)

2.2转移规则

每⼀步只能让农夫带着⼀个物品过河或⾃⼰⼀个⼈过河。且必须确保每次转移后,狼和⽺、⽺和菜不单独留下, 也就是: 如果农夫不在场,且狼和⽺在同⼀岸边,步骤⽆效 如果农夫不在场,且⽺和菜在同⼀岸边,步骤⽆效

2.3状态空间图

每个状态代表⼀个图中的节点,状态转移代表边。每次有效的转移构成从⼀个状态到另⼀个状态的路径。

 

 

3 Dijkstra算法

3.1步骤

1. 初始化起始状态为(0, 0, 0, 0)(所有物品在左岸)。

2. ⽬标状态为(1, 1, 1, 1)(所有物品都到右岸)。

3. 使⽤优先队列来选择最短路径。每次选择⼀个当前最短路径的状态进⾏扩展。

4. 对于当前状态,计算所有可能的转移(即合法的neighbor状态)。

5. 如果neighbor状态尚未访问过或找到了更短路径,则更新路径。

6. 重复步骤 3-5,直到找到⽬标状态或⽆法找到路径。

3.2伪代码

初始化起点距离为 0,其他点距离为无限大

初始化结点为 None

未访问 = {起点}

已访问 = {}

当未访问不为空:

        找到未访问中距离最小的点

        如果当前点是目标点,停止

         遍历当前点的所有合法neighbor:

                如果neighbor未访问过:

                        计算新距离

                        如果新距离更短,更新neighbor的距离和结点

                         将neighbor加入未访问集合

重建路径: 从目标点回溯到起点

返回路

4.python代码 

# Dijkstra类 修改自 示例代码7 单起点最短路径的Dijkstra's算法
class Dijkstra():
    def __init__(self):
        self.distances = {}
        self.previous = {}
    def is_valid_state(self, state):
        """
        判断能不能过河
        """
        farmer, wolf, goat, cabbage = state
        # 狼会吃羊、羊会吃菜
        if (wolf == goat and farmer != wolf) or (goat == cabbage and farmer != goat):
            return False
        return True
    def get_neighbors(self, state):
        """
        获取当前状态的所有合法neighbor状态
        """
        farmer, wolf, goat, cabbage = state
        neighbors = []
        
        # 尝试过河
        possible_moves = [
            (farmer ^ 1, wolf, goat, cabbage),      # 农夫自己过河
            (farmer ^ 1, wolf ^ 1, goat, cabbage),  # 农夫带着狼过河
            (farmer ^ 1, wolf, goat ^ 1, cabbage),  # 农夫带着羊过河
            (farmer ^ 1, wolf, goat, cabbage ^ 1),  # 农夫带着菜过河
        ]
        
        # 添加所有合法的neighbor状态
        for move in possible_moves:
            if self.is_valid_state(move):
                neighbors.append(move)
        
        return neighbors
    def find_min_distance(self, unvisited):
        """
        找最小距离
        """
        min_distance = float('inf')
        min_state = None
        for state in unvisited:
            if self.distances.get(state, float('inf')) < min_distance:
                min_distance = self.distances[state]
                min_state = state
        return min_state
    def dijkstra(self, start, goal):
        """
        Dijkstra算法
        """
        # 初始化所有距离
        self.distances[start] = 0
        self.previous[start] = None
        
        unvisited = {start}
        visited = set()
        while unvisited:
            # 找最小距离
            current_state = self.find_min_distance(unvisited)
            unvisited.remove(current_state)
            visited.add(current_state)
            # 到达目标后停止搜索
            if current_state == goal:
                break
            # 遍历
            for neighbor in self.get_neighbors(current_state):
                if neighbor in visited:
                    continue
                # 计算距离
                new_distance = self.distances[current_state] + 1
                if neighbor not in self.distances or new_distance < self.distances[neighbor]:
                    self.distances[neighbor] = new_distance
                    self.previous[neighbor] = current_state
                    unvisited.add(neighbor)
        # 重建路径
        path = []
        current_state = goal
        while current_state is not None:
            path.append(current_state)
            current_state = self.previous.get(current_state)
        
        return path[::-1]  # 反转路径
# 格式化,更易读
def format_state(state):
    farmer, wolf, goat, cabbage = state
    
    # 河
    river = "~~~~~~"
    
    left_side = []
    right_side = []
    if farmer == 0:
        left_side.append("农夫")
    else:
        right_side.append("农夫")
    if wolf == 0:
        left_side.append("狼")
    else:
        right_side.append("狼")
    if goat == 0:
        left_side.append("羊")
    else:
        right_side.append("羊")
    if cabbage == 0:
        left_side.append("菜")
    else:
        right_side.append("菜")
    
    # 输出到左右两侧
    left_output = " ".join(left_side) if left_side else ""
    right_output = " ".join(right_side) if right_side else ""
    
    return f"{left_output} | {river} | {right_output}"
 # 起始状态
start_state = (0, 0, 0, 0)  # 农夫、狼、羊、菜都在左边
# 目标状态
goal_state = (1, 1, 1, 1)   # 农夫、狼、羊、菜都到右边
# 执行代码
dijkstra_solver = Dijkstra()
solution_path = dijkstra_solver.dijkstra(start_state, goal_state)
if solution_path:
    for state in solution_path:
        print(state,format_state(state))
else:
    print("没有找到可用的路径")

 运行结果

 

 5总结

        在求解此问题过程中,我使⽤了Dijkstra算法来找到从起始状态到⽬标状态的最短路径。利⽤Dijkstra算法可以保证 在合法状态(狼不吃⽺、⽺不吃菜)下通过最少的步骤完成从起始状态到⽬标状态的任务。

        ⾸先进⾏数学建模,将农夫、狼、⽺和菜的状态视作⼀个四元组(farmer, wolf, goat, cabbage),其中每个 元素的值为0或1,表⽰该物品是否在左边0或右边1。起始状态为(0, 0, 0, 0),⽬标状态为(1, 1, 1, 1),即所有物品都从左边移到右边。

        通过对状态空间建模,可以将每⼀种合法的状态转化为图中的⼀个节点,状态间的合法转换则对应图中的边。再 利⽤Dijkstra算法通过求解图中从起点(0, 0, 0, 0)到⽬标(1, 1, 1, 1)的最短路径,找出最优⽅案。

        Dijkstra算法通⽤性很强。但考虑到状态空间不⼤(共2^4=16种状态),深度优先搜索DFS和⼴度优先搜索BFS 也可以⽤于此问题的求解;且状态空间增⼤时,Dijkstra算法可能会变得很慢,这时候可以利⽤A-star算法等启发 式函数加速搜索过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据蟒行探索者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值