【问题标题】:Correct implementation of Min-Max in a tree在树中正确实现 Min-Max
【发布时间】:2017-09-05 07:04:16
【问题描述】:

我在树上实现了最小-最大算法。我创建的算法按预期工作,返回树中对应于最小值和最大值的路径。但我的问题是感觉不对。
我的问题是,这是 min-max 算法的正确实现吗?
我无法在由节点组成的树结构中找到该算法的纯实现。在我设法找到的示例中,人们实施了算法来解决与游戏相关的问题(井字游戏)。老实说,我觉得自己很愚蠢,无法建立联系。
我使用了 2 个函数,
1->evaluations_function 遍历树并将值添加到没有的节点(也就是所有不是叶子的节点)。
2->print_optimal 遍历树并返回路径。

public void evaluation_function(Node root, int maxHeight, int rootHeight) {
        if (root.childrenList.Count != 0) {
            foreach (Node node in root.childrenList) {
                evaluation_function(node, maxHeight, rootHeight);
            }
        }
        if (root.height != rootHeight) {
            if ((root.height % 2) == 0) {
                if (root.value < root.parent.value || root.parent.value == 123456) {
                    root.parent.value = root.value;
                }
            } else if ((root.height % 2) != 0) {
                if (root.value > root.parent.value || root.parent.value == 123456) {
                    root.parent.value = root.value;
                }
            }
        }
    }

这里是 print_optimal

public void print_optimal(Node root) {
        int maxValue = 0;
        int minValue = 9999;
        if (root.childrenList.Count != 0) {
            if (root.height % 2 == 0) {
                foreach (Node child in root.childrenList) {
                    if (child.value > maxValue) { maxValue = child.value; }
                }
                foreach (Node child in root.childrenList) {
                    if (child.value == maxValue) {
                        Console.WriteLine("Selected node " + child.name
                            + " with value = " + child.value + " as MAX player");
                        print_optimal(child);
                    }
                }
            } else {
                foreach (Node child in root.childrenList) {
                    if (child.value < minValue) { minValue = child.value; }
                }
                foreach (Node child in root.childrenList) {
                    if (child.value == minValue) {
                        Console.WriteLine("Selected node " + child.name
                            + " with value = " + child.value + " as MIN player");
                        print_optimal(child);
                    }
                }
            }
        }
    }

我不想用代码来解决这个问题,因为我的问题是理论上的。但是,如果您想查看数据结构或想测试算法,您可以在这里找到它:https://github.com/AcidDreamer/AI_Exercises/tree/master/1.2.1/Exercise1_2_1/Exercise1_2_1


改进
评估函数更改为

public void evaluation_function(Node root, int rootHeight) {
        if (root.childrenList.Count != 0) {
            foreach (Node node in root.childrenList) {
                evaluation_function(node, rootHeight);
            }
        }
        if (root.height != rootHeight || root.childrenList.Count != 0) {
            int maxValue = 0, minValue = 999;
            if ((root.height % 2) == 0) {
                foreach (Node child in root.childrenList) {
                    if (maxValue < child.value) {
                        maxValue = child.value;
                        root.value = maxValue;
                        root.bestChild = child;
                    }
                }
            } else if ((root.height % 2) != 0) {
                foreach (Node child in root.childrenList) {
                    if (minValue > child.value) {
                        minValue = child.value;
                        root.value = minValue;
                        root.bestChild = child;
                    }
                }
            }
        }
    }

为当前节点赋值。
print_optimal 使用 bestChild 字段有很大的改进,现在改为

public void print_optimal(Node root) {
        if (root.bestChild != null) {
            Console.Write(root.name + "->");
            print_optimal(root.bestChild);
        } else {
            Console.WriteLine(root.name);
        }
    }

【问题讨论】:

    标签: algorithm tree minmax


    【解决方案1】:

    我觉得接收节点作为输入的evaluation_function 实际上更新父节点的值有点不自然。

    我觉得更好的方法是让该函数更新当前节点。

    如果当前节点是叶子,它应该什么都不做。

    否则,它应该遍历所有子节点(就像它当前在第一个循环中所做的那样),并且在每次递归调用一个子节点之后,它还应该检查当前节点值是否应该更新(ie 刚刚遇到了一个更好的值,对于当前的孩子)。

    此外,可以为每个节点添加一个附加字段,例如bestChildbestMove,以避免在调用print-optimal 时重新遍历子节点。

    【讨论】:

    • 您建议的 bestChild 确实是一个了不起的想法,感谢您的提及。我会根据您的建议进行处理,看看我能做些什么,完成后我会更新,感谢您的宝贵时间!
    • 不客气,期待您的回音!
    • 正如预期的那样,bestChild 是一种令人惊叹的生活品质。更新了问题。代码更易理解,更干净,非常感谢您的建议
    【解决方案2】:

    总体而言,您的代码很好(您知道,因为它有效),但我对改进有一些评论:

    evaluation_function:你根本不使用maxHeight。使用它或删除参数。该函数更改当前节点的父节点很奇怪(一种不好的做法)。它应该查看孩子的值(如果不是叶子),并根据它更新当前节点的值。此外,您可以添加一个字段来跟踪最佳子节点(甚至是叶子的选定路径)以避免print_optimal 中的匹配过程。

    print_optimal():如果 2 个孩子具有相同的值,您目前会遇到问题:您将打印两个孩子并探索两棵树。检查if (child.value &lt; minValue) { minValue = child.value; } 是没有用的,因为你假设你已经使用了evaluation_function(),如果你忘记了“替换”evaluation_function 是不够的。

    【讨论】:

    • 我删除了 maxHeight (在一些测试后忘记删除它),你和 @quertyman 的响应是相似的,所以我将致力于交换当前节点值。我将使用 bestChild 字段帮助我探索树,感谢您抽出宝贵的时间回复!
    • 第一次你应该明确删除 maxHeight。如果将来您想从事大型游戏并拥有更广泛使用的算法,那么当您负担不起(或只是不能)完全探索树到叶子时,您将再次需要 maxHeight
    • 希望将来我能够在更广泛的问题中使用该逻辑。更新了问题。
    • 代码现在好多了,恭喜!但是,现在 rootHeight 不再有用了,您可以将其删除。
    猜你喜欢
    • 2023-03-19
    • 2017-09-12
    • 1970-01-01
    • 2021-11-27
    • 2020-09-09
    • 2020-08-04
    • 2014-07-11
    • 2022-06-12
    • 2015-03-09
    相关资源
    最近更新 更多