【问题标题】:Same result for three arrays of counters of BFS and two A* with different heuristicsBFS 的三个计数器数组和具有不同启发式的两个 A* 的结果相同
【发布时间】:2016-10-31 19:54:16
【问题描述】:

我正在尝试构建一个程序,在 15 人游戏中比较算法 BFS 和两个不同 A*(具有两个启发式)的笔画数。

我的问题是计数器对 BFS 和 A* 的三个计数器数组计数相同的结果。然而,我实际上使用了来自主(项目类)的三个不同数组,并且为这些笔画分配了三个不同的变量。

我认为问题来自 A*。对于他们每个人,我从父亲开始,然后计算其启发式并将他添加到边界。

虽然边界不是空的,但我看看初始状态是否是 15 游戏的最终状态,否则我将其从边界中移除并寻找他的儿子。我为所有这些设置了它们的 g 值,我计算了它们的每一个启发式算法以最终得到它们的 f 值。如果他们还没有在边境,我把他们加进去。然后我将frontier中第一个的F值与frontier中的每一个进行比较,并将F值最好的一个实例化为current_state。

        int best_f_value=frontier.get(0).getFValue();

        for(State s : frontier){

            if(s.getFValue()<=best_f_value){
                best_f_value=s.getFValue();
                current_state=s;

            }

        }

然而,在寻找计数器时,我总是对 BFS 和 A* 有相同的笔画数,其中包含错位瓷砖数量的启发式,而 A* 与曼哈顿距离的启发式相同。这可能会出现一次,但并非总是如此!

我认为问题在于 A* 的函数,而不是用于计算其启发式的函数。因此,这里是第二个 A* 的代码,他与第一个相似,但有错位的磁贴。

第二个 A* 代码

public State aStar2(){

    State child;
    System.out.println("we entered A_star with Manhattan distance");
    System.out.println("final:\n"+finalState);
    /** current state of dfs algorithm **/
    State current_state;
    current_state = initialState;
    current_state.computeHeuristic2();
    current_state.computeValueF2();
    //int best_f_value= current_state.getFValue();
    int current_state_g_value = 0;
    System.out.println(initialState);

    // get alwhile(!frontier.isEmpty()){l possible actions from the given node
    List<Action> actions = current_state.getActions();
    //frontier is a Stack displaying not currently explored nodes
    LinkedList<State> frontier = new LinkedList<State>();
    // frontier already contains the first node
    frontier.push(initialState);

    // explored_nodes contains all explored nodes.
    LinkedList<State> explored_nodes = new LinkedList<State>();

    // this List is used to show the path
    LinkedList<State> path = new LinkedList<State>();

    while(!frontier.isEmpty()){
        number_of_strokes_A2+=1;

        // we found the goal
        if(goal_test(current_state)){
            for(State visited :path){
                System.out.println(visited);
            }
            array_number_of_strokes_A2.add(number_of_strokes_A2);
            System.out.println("nombre de coups A2 : " + number_of_strokes_A2);
            number_of_strokes_A2=0;
            System.out.println("on a réussi : \n" + current_state);


            return current_state;
        }
        // We remove the current state from the frontier
        // VERIFY THIS IS OKAY !!!
        frontier.remove(current_state);
        // We get all possible actions from the current state
        actions = current_state.getActions();
        // We add the current state to already explored nodes
        explored_nodes.add(current_state);
        //System.out.println(current_state);
        path.add(current_state);


        current_state_g_value = current_state.getValueG();
        // We create every child
        for (Action action : actions){
            // we get a child from the execution of the current_state
            child = current_state.execute(action);
            child.setValueG(current_state_g_value);
            child.computeHeuristic2();
            child.computeValueF2();             
            if(!explored_nodes.contains(child)&&!frontier.contains(child)){
                // This child not being already explored nor int the frontier we add it to the last one
                frontier.add(0,child);
            }
        }

        int best_f_value=frontier.get(0).getFValue();

        for(State s : frontier){

            if(s.getFValue()<=best_f_value){
                best_f_value=s.getFValue();
                current_state=s;

            }

        }       

    }

    return finalState;

}

询问我是否需要比较第一个 A*。 下面是启发式方法。

启发式

它们在另一个文件中。我不认为他们有罪。

public void computeValueF(){
    // to be completed
    f_value = getValueG() + getHeuristic();
}

public void computeValueF2(){
    // to be completed
    f_value = getValueG() + getHeuristic2();
}

public int getFValue(){
    return f_value;
}

@Override
public int getFValue2(){
    return f_value;
}

public int getHeuristic(){
    return h_value;
}

public int getHeuristic2(){
    //somme de la distance de Manhattan entre lemplacement couvrant de chaque case � sa position finale
    int h2=0;
    for(int i=0;i<9;i++){
        h2+=Integer.valueOf(puzzle.charAt(i))%3-i%3+Integer.valueOf(puzzle.charAt(i))/3-i/3;
    }
    return h2;
    // to be completed
}

@Override
public int compareTo(Searchable<Puzzle, PuzzleAction> o) {
    // TODO Auto-generated method stub
    if(this.getHeuristic()> o.getHeuristic());
    return 0;
}

@Override
public void setValueG(int cost) {
    // TODO Auto-generated method stub
    g_value = cost+1;
}

@Override
public int getValueG() {
    // TODO Auto-generated method stub
    return g_value;
}

@Override
public void computeHeuristic() {
    // TODO Auto-generated method stub
    //nombre de cases mal placees
    for(int i=0;i<9;i++){
        if((Integer.valueOf(puzzle.charAt(i))-i)!=0){
            h_value+=1;
        }
    }
}

@Override
public void computeHeuristic2() {
    // TODO Auto-generated method stub
    int h2=0;
    for(int i=0;i<9;i++){
        h2+=Integer.valueOf(Math.abs(puzzle.charAt(i))%3-i%3)+Math.abs(Integer.valueOf(puzzle.charAt(i))/3-i/3);
    }
    this.h2_value=h2;
}

结果

以下是笔画数的结果:

strokes BFS : [9, 7, 33, 33, 53, 53, 51]
strokes AStar1[9, 7, 33, 33, 53, 53, 51]
strokes AStar2[9, 7, 33, 33, 53, 53, 51]

参考文献

这个问题与this one answered by Ishamael 类似,但不同之处在于实现 BFS 和 DFS 的区别

更新:尝试从实际当前位置到目标的启发式方法

Ishmael 对我的启发式方法保持不变是正确的。因此,我尝试修改启发式计算方法,以参考当前状态,而不是永远不会改变的东西。这是 Puzzle.java 中的方法

@Override
public void computeHeuristic1(String s) {
    // TODO Auto-generated method stub
    //nombre de cases mal placees
    h1_value=0;
    for(int i=0;i<9;i++){
        if((Integer.valueOf(s.charAt(i))-i)!=0){
            h1_value+=1;
        }
    }
}

我们将在 Problem.java 中使用

child.computeHeuristic1(child.toString());

如果需要,我可以添加更多代码。

我得到了那些启发式方法:

array_heuritics_1 : [9, 9, 9, 9, 9, 9, 9, 9
array_heuritics_2 : [130, 131, 131, 129, 129, 128, 1

最后一个是异常的,因为每个图块最多可以带来 3+3 的启发式值。因此,具有启发式 > 51 的东西是站不住脚的。

所以我试图展示测试在做什么,我发现了一些有趣的东西:

the child.toString : 
1 2 5 
3 4 . 
6 7 8 

the value : 
i : 0 & s.charAt(i) : 1
i : 1 & s.charAt(i) : .
i : 2 & s.charAt(i) : 2
i : 3 & s.charAt(i) : 

i : 4 & s.charAt(i) :  
i : 5 & s.charAt(i) :  
i : 6 & s.charAt(i) :  
i : 7 & s.charAt(i) : 6
i : 8 & s.charAt(i) : 7
i : 0 & s.charAt(i) : 1
i : 1 & s.charAt(i) : .
i : 2 & s.charAt(i) : 2
i : 3 & s.charAt(i) : 

i : 4 & s.charAt(i) :  
i : 5 & s.charAt(i) :  
i : 6 & s.charAt(i) :  
i : 7 & s.charAt(i) : 6
i : 8 & s.charAt(i) : 7

其实我们不是按数字来做测试,而是按字符来做测试,而且还有一些空格!

【问题讨论】:

  • 您在理解 a* 方面的差距超出了适合 SO 的问题范围
  • @ControlAltDel 我不同意你的观点,我认为 OP 试图解释他是如何考虑编写 a* 而不是解释 a* 的,因此是合适的。你的意思是他应该把它发布在 CS.SE 上?

标签: java arrays algorithm a-star


【解决方案1】:

首先,让我们考虑一下如果你的 A* 没有使用任何启发式算法会发生什么(因为它,如果 getFValue 刚刚返回 getValueG)。请注意,当算法启动时,它在frontier 中只有一个节点,因此将被选中。在每次连续迭代中,前沿中的值将按cost 降序排序(如果您在纸上画几个例子,您可以通过归纳来证明),并且前沿中第一个元素的成本将要么相等到最后一个或一个更大的成本。现在,当您运行选择state 的循环时,您将始终选择最后一个元素,因为它将具有最小的FValue,并且您始终选择最后一个。换句话说,如果启发式没有到位,您的 A* 的行为将与您的 BFS 完全相同(它已经选择了最后一个元素)。

现在假设你的启发式只是一个常数,例如你的 getFValue 被实现为 return getValueG + constant。您可以通过遵循与上述非常相似的逻辑再次证明它的行为就好像它只是 BFS - 如果您将一个常量添加到您比较的值,它们将以相同的方式进行比较。

最后,很容易证明两个启发式方法总是返回相同的值。请注意,它们仅依赖于puzzle,它是恒定的。它们绝不依赖于代理的当前位置。曼哈顿距离启发式应该是从 当前位置 到地图上所有目标位置的曼哈顿距离相加(或者更确切地说,只是到最近的位置作为启发式更有意义)。相反,它计算的东西根本不依赖于当前位置,这是错误的。而且由于它返回的值总是相同的,由于我上面提供的原因,它的行为类似于 BFS。

【讨论】:

  • 是的,您的启发式方法当然应该取决于当前状态。如果您知道目标的坐标,则有意义的启发式算法将是 abs(goalX - currentX) + abs(goalY - currentY),这正是从目标到当前位置的曼哈顿距离。如果你有多个目标,那就更棘手了。
  • 我不知道为什么,但现在只有 A*1 带有错位的块启发式和 BFS 有不同的结果......我试图在我的启发式中添加一些 .this 以更新状态我们正在计算它,但它没有奏效。 A*2 发生了一些变化,我无法找到...
  • 但你是对的,启发式算法对于 A* 1 保持不变,对于 A*2 有所不同
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 2017-07-19
  • 2019-05-07
  • 2017-04-16
相关资源
最近更新 更多