A*算法————传教士和野人

还有一篇更好的解释,我不太明白为什么评价函数是这样...

http://wenku.baidu.com/link?url=gKyphJnwWf04XJPszGPb9plPPncvpw5c9omOTlzCXQzHCid_saeiIy8_fIkMYpKvUXvEAuW7uSC1l_g2zm1U5TUB4874ZcTm-P_iB0SG_Na

转载自

Maruko_momoko忘的博客

A*算法————传教士和野人
 问题描述

设有3个传教士和3个野人来到河边,打算乘一只船从左岸渡到右岸去。该船的负载能力为两人。在任何时候,如果野人人数超过传教士人数,那么野人就会把传教士吃掉。他们怎样才能用这条船安全地把所有人都渡河过去?

问题表示:需要考虑两岸的修道士人数和野人人数,船的位置。用三元式表示状态:S= (m, n, B)

其中,m表示左岸修道士人数,n表示左岸野人人数,B表示左岸船的数目。


评估函数的建立。
评估函数为f=d+h=d+M+N-2*B
M表示左岸的传教士的人数,N表示左岸野人的数目,B取值为0或1 。1表示船在左岸,0 表示船在右岸。d 表示节点的深度。
 

下面我们来证明h(n)=M+C-2B是满足A*条件的。
  我们分两种情况考虑。先考虑船在左岸的情况。如果不考虑限制条件,也就是说,船一次可以将三人从左岸运到右岸,然后再有一个人将船送回来。这样,船一个来回可以运过河2人,而船仍然在左岸。而最后剩下的三个人,则可以一次将他们全部从左岸运到右岸。所以,在不考虑限制条件的情况下,也至少需要摆渡[(M+N-3)/2]*2+1次。其中分子上的"-3"表示剩下三个留待最后一次运过去。除以"2"是因为一个来回可以运过去2人,需要[(M+N-3)/2]个来回,而"来回"数不能是小数,需要向上取整,这个用符号[ ]表示。而乘以"2"是因为一个来回相当于两次摆渡,所以要乘以2。而最后的"+1",则表示将剩下的3个运过去,需要一次摆渡。
化简有: M+N-2。
  再考虑船在右岸的情况。同样不考虑限制条件。船在右岸,需要一个人将船运到左岸。因此对于状态(M,N,0)来说,其所需要的最少摆渡数,相当于船在左岸时状态(M+1,N,1)或(M,N+1,1)所需要的最少摆渡数,再加上第一次将船从右岸送到左岸的一次摆渡数。因此所需要的最少摆渡数为:(M+N+1)-2+1。其中(M+N+1)的"+1"表示送船回到左岸的那个人,而最后边的"+1",表示送船到左岸时的一次摆渡。
化简有:(M+N+1)-2+1=M+N。
  综合船在左岸和船在右岸两种情况下,所需要的最少摆渡次数用一个式子表示为:M+N-2B。其中B=1表示船在左岸,B=0表示船在右岸。
  由于该摆渡次数是在不考虑限制条件下,推出的最少所需要的摆渡次数......
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 好的,以下是使用A*算法解决野人传教士问题的Java代码: 首先,定义一个节点类,表示当前状态和移动到此状态的代价: ```java public class Node implements Comparable<Node> { int[] state; // 当前状态 Node parent; // 父节点 int cost; // 从起点到当前节点的代价 int heuristic; // 启发式函数值 public Node(int[] s, Node p, int c, int h) { state = s; parent = p; cost = c; heuristic = h; } // 比较两个节点的代价和启发式函数值,用于优先队列排序 public int compareTo(Node other) { return (cost + heuristic) - (other.cost + other.heuristic); } } ``` 然后,定义一个搜索函数,使用优先队列实现A*算法: ```java public static void aStarSearch() { int[] init = {3, 3, 1}; // 初始状态 int[] goal = {0, 0, 0}; // 目标状态 PriorityQueue<Node> queue = new PriorityQueue<Node>(); // 优先队列 queue.add(new Node(init, null, 0, heuristic(init))); // 将初始状态加入队列 while (!queue.isEmpty()) { Node current = queue.poll(); // 取出代价最小的节点 // 到达目标状态,输出路径 if (Arrays.equals(current.state, goal)) { printPath(current); return; } // 扩展当前节点,生成所有可能的子节点 ArrayList<Node> children = expand(current); // 将子节点加入队列 for (Node child : children) { queue.add(child); } } System.out.println("No solution found."); } ``` 接下来,实现启发式函数,计算当前状态到目标状态的估计代价: ```java public static int heuristic(int[] state) { int cost = 0; // 野人传教士分别需要过河的数量 int num_missionaries = state[0]; int num_cannibals = state[1]; // 判断野人是否会攻击传教士,如果会则需要多一艘船 if ((num_missionaries > 0 && num_missionaries < num_cannibals) || (num_missionaries < 0 && num_missionaries > num_cannibals)) { cost += 1; } // 计算野人传教士需要过河的次数 cost += (Math.abs(num_missionaries) + Math.abs(num_cannibals) + state[2] - 1) / 2; return cost; } ``` 最后,实现扩展函数,生成所有可能的子节点: ```java public static ArrayList<Node> expand(Node node) { ArrayList<Node> children = new ArrayList<Node>(); int[] state = node.state; int boat = state[2]; // 生成所有可能的移动方式 for (int i = 0; i <= 2; i++) { for (int j = 0; j <= 2; j++) { if (i + j <= 2 && i + j >= 1) { int[] new_state = state.clone(); new_state[0] += (boat == 1 ? -i : i); new_state[1] += (boat == 1 ? -j : j); new_state[2] = 1 - boat; if (isValid(new_state)) { // 判断移动是否合法 Node child = new Node(new_state, node, node.cost + 1, heuristic(new_state)); children.add(child); } } } } return children; } public static boolean isValid(int[] state) { int num_missionaries = state[0]; int num_cannibals = state[1]; // 判断野人是否会攻击传教士 if ((num_missionaries > 0 && num_missionaries < num_cannibals) || (num_missionaries < 0 && num_missionaries > num_cannibals)) { return false; } // 判断过河的人数是否超过船的容量 if (Math.abs(num_missionaries) + Math.abs(num_cannibals) > 2) { return false; } // 判断传教士数量是否为负数 if (num_missionaries < 0 || num_cannibals < 0) { return false; } return true; } ``` 完整代码如下: ```java import java.util.*; public class AStar { public static void main(String[] args) { aStarSearch(); } public static void aStarSearch() { int[] init = {3, 3, 1}; // 初始状态 int[] goal = {0, 0, 0}; // 目标状态 PriorityQueue<Node> queue = new PriorityQueue<Node>(); // 优先队列 queue.add(new Node(init, null, 0, heuristic(init))); // 将初始状态加入队列 while (!queue.isEmpty()) { Node current = queue.poll(); // 取出代价最小的节点 // 到达目标状态,输出路径 if (Arrays.equals(current.state, goal)) { printPath(current); return; } // 扩展当前节点,生成所有可能的子节点 ArrayList<Node> children = expand(current); // 将子节点加入队列 for (Node child : children) { queue.add(child); } } System.out.println("No solution found."); } public static int heuristic(int[] state) { int cost = 0; // 野人传教士分别需要过河的数量 int num_missionaries = state[0]; int num_cannibals = state[1]; // 判断野人是否会攻击传教士,如果会则需要多一艘船 if ((num_missionaries > 0 && num_missionaries < num_cannibals) || (num_missionaries < 0 && num_missionaries > num_cannibals)) { cost += 1; } // 计算野人传教士需要过河的次数 cost += (Math.abs(num_missionaries) + Math.abs(num_cannibals) + state[2] - 1) / 2; return cost; } public static ArrayList<Node> expand(Node node) { ArrayList<Node> children = new ArrayList<Node>(); int[] state = node.state; int boat = state[2]; // 生成所有可能的移动方式 for (int i = 0; i <= 2; i++) { for (int j = 0; j <= 2; j++) { if (i + j <= 2 && i + j >= 1) { int[] new_state = state.clone(); new_state[0] += (boat == 1 ? -i : i); new_state[1] += (boat == 1 ? -j : j); new_state[2] = 1 - boat; if (isValid(new_state)) { // 判断移动是否合法 Node child = new Node(new_state, node, node.cost + 1, heuristic(new_state)); children.add(child); } } } } return children; } public static boolean isValid(int[] state) { int num_missionaries = state[0]; int num_cannibals = state[1]; // 判断野人是否会攻击传教士 if ((num_missionaries > 0 && num_missionaries < num_cannibals) || (num_missionaries < 0 && num_missionaries > num_cannibals)) { return false; } // 判断过河的人数是否超过船的容量 if (Math.abs(num_missionaries) + Math.abs(num_cannibals) > 2) { return false; } // 判断传教士数量是否为负数 if (num_missionaries < 0 || num_cannibals < 0) { return false; } return true; } public static void printPath(Node node) { ArrayList<Node> path = new ArrayList<Node>(); while (node != null) { path.add(node); node = node.parent; } Collections.reverse(path); System.out.println("Solution:"); for (Node n : path) { System.out.println(Arrays.toString(n.state)); } } } class Node implements Comparable<Node> { int[] state; // 当前状态 Node parent; // 父节点 int cost; // 从起点到当前节点的代价 int heuristic; // 启发式函数值 public Node(int[] s, Node p, int c, int h) { state = s; parent = p; cost = c; heuristic = h; } // 比较两个节点的代价和启发式函数值,用于优先队列排序 public int compareTo(Node other) { return (cost + heuristic) - (other.cost + other.heuristic); } } ``` 这段代码可以求出野人传教士问题的最优解,欢迎测试。 ### 回答2: A*算法是一种常用于搜索和路径规划的算法。在野人传教士问题中,我们需要解决的是如何将三个野人和三个传教士安全地通过一艘只能容纳两个人的小船渡河,在任意一岸,野人的数量不能超过传教士的数量。为了解决这个问题,我们可以使用A*算法。 首先,我们需要定义状态。在这个问题中,一个状态可以由野人传教士的位置以及船的位置来表示。我们可以将其表示为一个由七个数值组成的元组,如(3, 3, 1, 0, 0, 0, 0),前两个数字表示左岸的野人传教士的数量,接下来两个数字表示右岸的野人传教士的数量,然后是船在左岸和右岸的位置表示。 接下来,我们需要定义启发函数。在这个问题中,我们可以使用每一步移动的成本作为启发函数。每一步移动的成本都为1,因为我们希望尽量少移动。我们还需要定义从一个状态到另一个状态的转换规则,即规定当只能船移动时,如何改变状态。 然后,我们将初始状态设为(3, 3, 1, 0, 0, 0, 0),目标状态设为(0, 0, 0, 3, 3, 1, 1)。我们可以使用A*算法来搜索从初始状态到目标状态的路径。在搜索过程中,我们根据启发函数的值选择下一个访问的状态,直到找到目标状态或者无法找到路径。 为了实现A*算法,我们可以使用优先队列来保存待访问的状态,并计算每个状态的启发函数值。根据启发函数的值,我们选择具有最小启发函数值的状态进行扩展,直到找到目标状态。 总的来说,A*算法可以用于解决野人传教士问题。我们可以根据定义的状态、启发函数和转换规则,使用A*算法来搜索并找到从初始状态到目标状态的路径。 ### 回答3: A*算法是一种用于解决搜索问题的启发式搜索算法,可以用于解决野人传教士问题。在野人传教士问题中,有3个野人和3个传教士需要过河,但是河边只有一个船,且每次船上最多只能载两个人。在任何一边,如果野人的数量超过传教士的数量,野人就会吃掉传教士。目标是找到一种过河方案,让所有人都安全到达对岸。 使用A*算法求解野人传教士问题,需要定义状态表示、评估函数和操作集合。 1. 状态表示:状态包括目前河岸上野人传教士的数量,以及船的位置(左岸或右岸)。 2. 评估函数:为了找到最优解,需要定义一个评估函数来评估每个状态的优先级。这个函数需要考虑到野人传教士的数量以及船的位置。野人数量和传教士数量的不平衡度越小,评估函数的值越小,表示该状态越优。 3. 操作集合:采取的操作有将1或2个人从一边带到另一边(如果合法)。每个操作会生成一个新状态,并根据评估函数为新状态赋予优先级。 通过以上三个步骤,可以使用A*算法解决野人传教士问题。首先,将初始状态放入一个优先级队列中。然后,循环执行以下步骤:从队列中取出优先级最高的状态,检查是否为目标状态;如果是,则找到了解决方案;否则,生成所有合法的下一步状态,并根据评估函数为它们赋予优先级,并将它们加入队列中。循环继续直到找到解决方案或队列为空。 最后,野人传教士问题可以用Java编程语言实现A*算法。可以使用图的搜索算法或基于状态的搜索算法来实现。在编程过程中,需要定义状态类、评估函数、操作集合以及优先级队列。通过递归或循环结构来实现算法的迭代过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值