Minimax由随机值产生意外低的结果

时间:2018-02-09 20:11:41

标签: java algorithm graph-algorithm minimax alpha-beta-pruning

我创建了minimax,pvs和alpha-beta算法,并使用随机树来遍历它们的结果。该树的每个父节点有[2,10]个子节点,总深度为10.每个叶子节点的随机值为[0,10]。

当我运行树遍历minimax算法时,结果值通常为2或3.这对我来说很奇怪,因为我猜它会给出5或者4或6,但它总是2或3.这是minimax算法它应该给出最小的最小值,这是令人困惑的,为什么它似乎几乎给出了整个树的最小值,因为我开始算法作为最大化的玩家。

结果如下:

Alpha Beta: 2.0, time: 10.606461 milli seconds
PVS: 2.0, time: 41.119652 milli seconds
Minimax: 2.0, time: 184.492937 milli seconds

这是源代码,不包括Timer类,因为这与我的问题无关。

import testing.utilities.data.Timer;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;

public class MinimaxAlphaBetaTest {
    public static void main(String[] args) {
        Node parent = new Node(0.);
        int depth = 10;
        createTree(parent,depth);
        Timer t = new Timer().start();
        double ab = alphabeta(parent,depth+1,Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY,true);
        t.stop();
        System.out.println("Alpha Beta: "+ab+", time: "+t.getTime());
        t = new Timer().start();
        double pv = pvs(parent,depth+1,Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY,1);
        t.stop();
        System.out.println("PVS: "+pv+", time: "+t.getTime());
        t = new Timer().start();
        double mm = minimax(parent,depth+1,true);
        t.stop();
        System.out.println("Minimax: "+mm+", time: "+t.getTime());
    }

    public static void createTree(Node n, int depth){
        if(depth == 0) {
            n.getChildren().add(new Node((double) randBetween(0, 10)));
            return;
        }
        for (int i = 0; i < randBetween(2,10); i++) {
            Node nn = new Node(0.);
            n.getChildren().add(nn);
            createTree(nn,depth-1);
        }
    }
    private static Random r;    // pseudo-random number generator
    private static long seed;        // pseudo-random number generator seed

    // static initializer
    static {
        // this is how the seed was set in Java 1.4
        seed = System.currentTimeMillis();
        r = new Random(seed);
    }
    public static int randBetween(int min, int max){
        return r.nextInt(max-min+1)+min;
    }

    public static double pvs(Node node, int depth, double alpha, double beta, int color){
        if(depth == 0 || node.getChildren().isEmpty())
            return color*node.getValue();
        int i = 0;
        double score;
        for(Node child : node.getChildren()){
            if(i++==0)
                score = -pvs(child,depth-1,-beta,-alpha,-color);
            else {
                score = -pvs(child,depth-1,-alpha-1,-alpha,-color);
                if(alpha<score || score<beta)
                    score = -pvs(child,depth-1,-beta,-score,-color);
            }
            alpha = Math.max(alpha,score);
            if(alpha>=beta)
                break;
        }
        return alpha;
    }

    public static double alphabeta(Node node, int depth, double alpha, double beta, boolean maximizingPlayer){
        if(depth == 0 || node.getChildren().isEmpty())
            return node.getValue();
        double v = maximizingPlayer ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
        for(Node child : node.getChildren()){
            if(maximizingPlayer) {
                v = Math.max(v, alphabeta(child, depth - 1, alpha, beta, false));
                alpha = Math.max(alpha, v);
            }else {
                v = Math.min(v, alphabeta(child, depth - 1, alpha, beta, true));
                beta = Math.min(beta, v);
            }
            if(beta <= alpha)
                break;
        }
        return v;
    }

    public static double minimax(Node node, int depth, boolean maximizingPlayer){
        if(depth == 0 || node.getChildren().isEmpty())
            return node.getValue();
        double v = maximizingPlayer ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
        for(Node child : node.getChildren()){
            if(maximizingPlayer)
                v = Math.max(v,minimax(child,depth-1,false));
            else
                v = Math.min(v,minimax(child,depth-1,true));
        }
        return v;
    }

    static class Node{
        List<Node> children = new ArrayList<>();
        double value;

        public Node(double value) {
            this.value = value;
        }

        public List<Node> getChildren() {
            return children;
        }

        public double getValue() {
            return value;
        }

        public void setValue(double value) {
            this.value = value;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Node node = (Node) o;
            return Double.compare(node.value, value) == 0;
        }

        @Override
        public int hashCode() {

            return Objects.hash(value);
        }
    }
}

修改

感谢Jiri Tousek评论,现在有意义,当用奇数的深度运行时给出一个通常高于5的数字和一个通常低于5的偶数,例如11,它会产生以下结果:

Alpha Beta: 7.0, time: 39.697701 milli seconds
PVS: 7.0, time: 216.849568 milli seconds
Minimax: 7.0, time: 998.207216 milli seconds

用奇数运行它,它看起来像深度3,结果来自我所见(5,6,7,8,9,10),深度5结果来自我所见过的(7,8,9),深度7结果来自我所见过的7或8,深度9结果来自我所见过的是8,深度11我见过7和8

而偶数产生{2,4,(2,3,4,5,6)},{6,8,10,(2,3)}

因此,在运行算法并查找结果时,轮到谁更重要?

IE如果它是最大化器,那么进入一个奇怪的深度,如果它是最小化器转向均匀深度,任何深度产生理想的移动?

1 个答案:

答案 0 :(得分:0)

由于你以最大值(在最顶层)开始并且向下达到10级,你将选择最深层次的最小值。

由于您选择[2-10]数字的最小(不是平均值),您不能指望它大致为5 - 它通常会更低。事实上,范围[0-10]中6个数字的最小值为5或更高的概率大约为(1/2)^6 ~ 1.5%

然后操作交替进行。我无法在那里计算任何好的概率,但直观地说,效果应该是中性的。

可能有助于查看在一次最小值的交替之后发生的事情。让我们每个节点有6个孩子(为了简单起见)。如前所述,&#34; min&#34;阶段将是5或更高,大约是(1/2)^6。对于&#34; max&#34;阶段导致5或更高,任何孩子足够5或更高。有6个孩子,所以机会大概是1 - (1 - (1/2)^6)^6 ~ 9%

这已经远远低于最底层5+数字的最初50%几率。然后,另一次迭代将导致1 - (1 - (0.09)^6)^6 ~ 3e-6概率为5+。