我正在处理来自ai类的任务,如下所示
我需要使用A *算法
我在0-9之间显示n位数
每个数字都用Ci标识,I = 0 ... n-1
在每个数字下面都有一个按钮,代理可以按下该按钮来修改它们的值。
每次代理按下按钮时,数字都会按照2条规则进行更改
Ci =(Ci + 1)%10
C(I + j)%n = [C(I + j)%n + k]%10
规则1)是代理人已知的,而规则2)则不是。常数j和k不为agenti
我的目标是从00 ... 0配置开始达到目标状态。
目标状态可以通过代理应用随机动作生成,也就是随机按下按钮,所以我确信至少有一种方法可以解决问题。
我的困难是: 如何将n位数显示表示为节点? 如何选择正确的启发式方法? 我对这次考试感到困惑和沮丧。
(对不起英文错误,我是意大利人!)
答案 0 :(得分:0)
A *算法的特征在于:
当您提出问题时,搜索树的每个节点似乎都是显示器的 n 数字的配置。请注意,这可以很容易地表示为大小为 n 的整数数组,其中数组的每个位置代表显示中的数字,该值表示显示中数字的实际值。例如,如果显示25847
的5位数字的显示可由[2, 5, 8, 4, 7]
表示。容易,对吧?
也就是说,你如何改变显示状态?如您所说,您只能使用每个数字下方的 n 按钮之一。因此,对于每个状态,如果您在搜索树上进行思考,您将拥有完全 n 可能的“邻居”或“后代”。您需要一个功能,为您提供按下特定按钮的节点(按下按钮可以模拟代理)。类似于以下内容(在Java中):
static int[] pressButton(int[] node, int button, int j, int k) {
int n = node.length;
int[] newNode = Arrays.copyOf(node, n);
newNode[button] = (newNode[button] + 1) % 10;
newNode[(button + j) % n] = (newNode[(button + j) % n] + k) % 10;
return newNode;
}
现在你有了这个,你可以用以下简单的东西生成当前节点的每个子节点:
for (int i = 0; i < node.length; i++) {
int[] newNode = pressButton(node, i, j, k);
// compute heuristic of the new node
// add it to the A* priority queue
}
现在你只需要一个启发式算法,它可以提供从给定节点到目标的距离的乐观估计。一个简单的想法是假设,对于给定节点,您必须按下按钮的次数至少与不同于目标的位数相同;例如,如果目标是41243
且当前节点为37253
,则必须按下按钮至少三次,因为有三个数字与目标不同。在Java中:
static int heuristic(int[] currentNode, int[] goal) {
int h = 0;
for (int i = 0; i < goal.length; i++) {
if (goal[i] != currentNode[i]) {
h = h + 1;
}
}
return h;
}
但请注意,此启发式错误。例如,如果目标是81730
,那么您当前的节点为71230
, j = 2且 k = 5,此启发式将给出一个值2;但是,我们只需按一下按钮就可以从当前节点到达目标状态,因此在这种情况下启发式将是悲观的。这是因为每次按下按钮时,会影响两个数字,这可能比我们想象的更快地获得解决方案。为了避免这种情况,我们可以从启发式中减去一个(如果它大于一个):
static int heuristic(int[] currentNode, int[] goal) {
int h = 0;
for (int i = 0; i < goal.length; i++) {
if (goal[i] != currentNode[i]) {
h = h + 1;
}
}
if (h > 1) {
h = h - 1;
}
return h;
}
更准确的启发式方法不仅可以根据与目标不同的位数来定义,还可以根据它们的差异来定义(差异越大,按下按钮所需的次数越多);但是,我不认为有一种简单的方法来定义基于此的乐观启发式(特别记住像 j = 0或 j = n ,否定 k 等)。如果你被允许在你的启发式中使用 j 和 k 的值(我假设没有,因为当你说这些是代理人不知道的时候,这就是我的解释),也许可能有一些复杂的空间,但即便如此,我也很难尝试定义它。
最后,鉴于问题的本质,当从一个节点到另一个节点时,很容易到达已经访问过的状态。如果要将树保持为实际树(并且是有限的)而不是无限图,则必须使用一组已访问过的节点,以避免在搜索空间中创建周期。