博弈树

news/2025/10/21 17:24:34/文章来源:https://www.cnblogs.com/xiaoxiongtaotao/p/19155952

模型介绍

博弈树是描述序贯博弈的数学工具,它将博弈过程表示为树形结构:

  • 节点:表示博弈状态;
  • 边:表示玩家的行动;
  • 叶子节点:表示博弈结束状态,包含收益值。

在两人零和博弈中,博弈树通常包含:

  • MAX节点:最大化玩家决策点;
  • MIN节点:最小化玩家决策点;
  • 终端节点:游戏结束状态。

历史背景

博弈树的概念最早出现在 \(20\) 世纪 \(40\) 年代,与博弈论和计算机科学的发展密切相关:

  • \(1944\) 年:冯·诺依曼和摩根斯坦在《博弈论与经济行为》中奠定了理论基础;
  • \(1950\) 年:香农提出计算机下棋的基本思路;
  • \(1956\) 年:约翰·麦卡锡提出Alpha-Beta剪枝算法;
  • \(1997\) 年:深蓝击败国际象棋世界冠军,展示了博弈树搜索的威力。

核心理论与证明

博弈树的基本性质

  • 定义:博弈树是一个有根树 \(T=(V,E)\),其中:
  1. 每个内部节点对应一个决策点;
  2. 每条边对应一个可能的行动;
  3. 每个叶子节点对应一个收益向量。
  • 定理 \(1\):任何有限完美信息博弈都可以用博弈树表示,证明:
  1. 博弈状态有限,可枚举所有可能状态;
  2. 每个状态通过有限行动转移到其他状态;
  3. 可构造树结构,根节点为初始状态;
  4. 叶子节点为终止状态,包含收益值。

Minimax 算法

  • 算法思想:
  1. MAX 玩家选择使收益最大化的行动;
  2. MIN 玩家选择使收益最小化的行动;
  3. 通过递归计算确定最优策略。
  • 数学表达:
Minimax(s) = if s是终端状态: return 效用值(s)if s是MAX节点: max(Minimax(子节点))if s是MIN节点: min(Minimax(子节点))
  • 正确性证明:
  • 引理:在两人零和博弈中,Minimax 值等于博弈的值,证明:
  1. 假设两个玩家都采用最优策略;
  2. 在 MAX 节点,MAX 会选择使最小值最大化的行动;
  3. 在 MIN 节点,MIN 会选择使最大值最小化的行动;
  4. 通过数学归纳法,可证明该值即为博弈值。

博弈树的复杂度

  • 定理 2:完全博弈树的节点数随游戏深度指数增长,证明,设分支因子为 \(b\),深度为 \(d\)
  1. 叶子节点数:\(b^d\)
  2. 总节点数:\(1+b+b^2+\cdots+b^d=\frac{b^{d+1}-1}{b-1}\approx O(b^d)\)
  • 例如国际象棋:
  1. \(b\approx35,d\approx100\)
  2. 节点数 \(\approx35^{100}\approx10^{154}\)(远大于宇宙原子数)

代码实现

基础博弈树实现

#include <bits/stdc++.h>
#define int long longusing namespace std;// 博弈树节点
class GameTreeNode {public:int value;         // 节点值(对于内部节点是计算值,叶子节点是实际值)bool is_max_node;  // true: MAX节点, false: MIN节点bool is_terminal;  // 是否为终端节点vector<shared_ptr<GameTreeNode>> children;GameTreeNode(int val, bool is_max, bool terminal = false): value(val), is_max_node(is_max), is_terminal(terminal) {}// 添加子节点void addChild(shared_ptr<GameTreeNode> child) {children.push_back(child);}// 显示节点信息void display(int depth = 0) {string indent(depth * 2, ' ');cout << indent << (is_max_node ? "MAX" : "MIN") << "节点";if (is_terminal) {cout << "[终端, 值=" << value << "]" << endl;} else {cout << "[值=" << value << "]" << endl;}for (auto& child : children) {child->display(depth + 1);}}
};// 博弈树构建器
class GameTreeBuilder {public:// 构建一个示例博弈树static shared_ptr<GameTreeNode> buildExampleTree() {/*构建如下博弈树:MAX/ | \MIN MIN MIN/|\  |\  |\3 5 1 2 9 0 8*/// 创建叶子节点(终端节点)auto leaf1 = make_shared<GameTreeNode>(3, false, true);auto leaf2 = make_shared<GameTreeNode>(5, false, true);auto leaf3 = make_shared<GameTreeNode>(1, false, true);auto leaf4 = make_shared<GameTreeNode>(2, false, true);auto leaf5 = make_shared<GameTreeNode>(9, false, true);auto leaf6 = make_shared<GameTreeNode>(0, false, true);auto leaf7 = make_shared<GameTreeNode>(8, false, true);// 创建MIN节点auto min1 = make_shared<GameTreeNode>(0, false);min1->addChild(leaf1);min1->addChild(leaf2);min1->addChild(leaf3);auto min2 = make_shared<GameTreeNode>(0, false);min2->addChild(leaf4);min2->addChild(leaf5);auto min3 = make_shared<GameTreeNode>(0, false);min3->addChild(leaf6);min3->addChild(leaf7);// 创建根节点(MAX节点)auto root = make_shared<GameTreeNode>(0, true);root->addChild(min1);root->addChild(min2);root->addChild(min3);return root;}
};// Minimax算法实现
class MinimaxSolver {public:// 计算博弈树的最优值int solve(shared_ptr<GameTreeNode> node) {if (node->is_terminal) {return node->value;}if (node->is_max_node) {int best_value = INT_MIN;for (auto& child : node->children) {int child_value = solve(child);best_value = max(best_value, child_value);}node->value = best_value;return best_value;} else {int best_value = INT_MAX;for (auto& child : node->children) {int child_value = solve(child);best_value = min(best_value, child_value);}node->value = best_value;return best_value;}}// 显示求解过程void solveWithTrace(shared_ptr<GameTreeNode> node, int depth = 0) {string indent(depth * 2, ' ');if (node->is_terminal) {cout << indent << "终端节点返回值: " << node->value << endl;return;}cout << indent << (node->is_max_node ? "MAX" : "MIN") << "节点开始计算" << endl;if (node->is_max_node) {int best_value = INT_MIN;for (auto& child : node->children) {solveWithTrace(child, depth + 1);best_value = max(best_value, child->value);cout << indent << "考虑子节点值: " << child->value << ", 当前最佳: " << best_value << endl;}node->value = best_value;} else {int best_value = INT_MAX;for (auto& child : node->children) {solveWithTrace(child, depth + 1);best_value = min(best_value, child->value);cout << indent << "考虑子节点值: " << child->value << ", 当前最佳: " << best_value << endl;}node->value = best_value;}cout << indent << (node->is_max_node ? "MAX" : "MIN") << "节点最终值: " << node->value << endl;}
};

Alpha-Beta 剪枝实现

// Alpha-Beta剪枝算法
class AlphaBetaSolver {public:int solve(shared_ptr<GameTreeNode> node, int alpha = INT_MIN, int beta = INT_MAX) {if (node->is_terminal) {return node->value;}if (node->is_max_node) {int value = INT_MIN;for (auto& child : node->children) {value = max(value, solve(child, alpha, beta));alpha = max(alpha, value);if (alpha >= beta) {break;  // Beta剪枝}}node->value = value;return value;} else {int value = INT_MAX;for (auto& child : node->children) {value = min(value, solve(child, alpha, beta));beta = min(beta, value);if (beta <= alpha) {break;  // Alpha剪枝}}node->value = value;return value;}}// 带详细跟踪的Alpha-Beta搜索int solveWithTrace(shared_ptr<GameTreeNode> node, int depth = 0, int alpha = INT_MIN, int beta = INT_MAX) {string indent(depth * 2, ' ');if (node->is_terminal) {cout << indent << "终端节点返回值: " << node->value << endl;return node->value;}cout << indent << (node->is_max_node ? "MAX" : "MIN") << "节点, alpha=" << alpha << ", beta=" << beta << endl;if (node->is_max_node) {int value = INT_MIN;for (int i = 0; i < node->children.size(); i++) {auto& child = node->children[i];cout << indent << "探索子节点 " << i << endl;int child_value = solveWithTrace(child, depth + 1, alpha, beta);value = max(value, child_value);alpha = max(alpha, value);cout << indent << "子节点" << i << "返回值: " << child_value << ", 当前值: " << value << ", alpha: " << alpha << endl;if (alpha >= beta) {cout << indent << "Beta剪枝! alpha=" << alpha << " >= beta=" << beta << endl;break;}}node->value = value;cout << indent << "MAX节点返回值: " << value << endl;return value;} else {int value = INT_MAX;for (int i = 0; i < node->children.size(); i++) {auto& child = node->children[i];cout << indent << "探索子节点 " << i << endl;int child_value = solveWithTrace(child, depth + 1, alpha, beta);value = min(value, child_value);beta = min(beta, value);cout << indent << "子节点" << i << "返回值: " << child_value << ", 当前值: " << value << ", beta: " << beta << endl;if (beta <= alpha) {cout << indent << "Alpha剪枝! beta=" << beta << " <= alpha=" << alpha << endl;break;}}node->value = value;cout << indent << "MIN节点返回值: " << value << endl;return value;}}
};

变种题目与解法

变种 1:多玩家博弈树

  • 问题:扩展到 \(3\) 个或更多玩家的博弈
  • 解法:使用向量表示多个玩家的收益
class MultiPlayerGameNode {public:array<int, 3> payoffs;  // 三个玩家的收益int current_player;     // 当前行动玩家 (0,1,2)bool is_terminal;vector<shared_ptr<MultiPlayerGameNode>> children;MultiPlayerGameNode(array<int, 3> p, int player, bool terminal = false): payoffs(p), current_player(player), is_terminal(terminal) {}
};class MultiPlayerSolver {public:// 多玩家Minimax(假设每个玩家最大化自己的收益)array<int, 3> solve(shared_ptr<MultiPlayerGameNode> node) {if (node->is_terminal) {return node->payoffs;}array<int, 3> best_payoffs;bool first_child = true;for (auto& child : node->children) {array<int, 3> child_payoffs = solve(child);if (first_child) {best_payoffs = child_payoffs;first_child = false;} else {// 当前玩家选择对自己最有利的行动if (child_payoffs[node->current_player] > best_payoffs[node->current_player]) {best_payoffs = child_payoffs;}}}return best_payoffs;}
};

变种 2:随机博弈树

  • 问题:包含概率事件的博弈(如掷骰子)
  • 解法:添加机会节点,计算期望值
class ChanceGameNode {public:enum NodeType { MAX_NODE,MIN_NODE,CHANCE_NODE,TERMINAL };NodeType type;int value;                                                  // 终端节点的值vector<pair<shared_ptr<ChanceGameNode>, double>> children;  // 子节点及其概率ChanceGameNode(NodeType t, int val = 0) : type(t), value(val) {}
};class ExpectiminimaxSolver {public:double solve(shared_ptr<ChanceGameNode> node) {switch (node->type) {case ChanceGameNode::TERMINAL:return node->value;case ChanceGameNode::MAX_NODE: {double best_value = -INFINITY;for (auto& [child, prob] : node->children) {best_value = max(best_value, solve(child));}return best_value;}case ChanceGameNode::MIN_NODE: {double best_value = INFINITY;for (auto& [child, prob] : node->children) {best_value = min(best_value, solve(child));}return best_value;}case ChanceGameNode::CHANCE_NODE: {double expected_value = 0.0;for (auto& [child, prob] : node->children) {expected_value += prob * solve(child);}return expected_value;}}return 0.0;}
};

变种 3:不完全信息博弈树

  • 问题:玩家无法观察到所有信息
  • 解法:使用信息集表示相同信息的决策点
class ImperfectInfoGameNode {public:string information_set;  // 信息集标识符bool is_terminal;int value;vector<shared_ptr<ImperfectInfoGameNode>> children;ImperfectInfoGameNode(string info_set, bool terminal = false, int val = 0): information_set(info_set), is_terminal(terminal), value(val) {}
};class ImperfectInfoSolver {private:unordered_map<string, double> strategy;  // 信息集到策略的映射public:// 使用反事实遗憾最小化等算法求解void solve(shared_ptr<ImperfectInfoGameNode> node,double reach_probability = 1.0) {if (node->is_terminal) {return;}// 简化实现:实际需要使用CFR等算法// 这里只是展示框架for (auto& child : node->children) {solve(child, reach_probability);}}
};

变种 4:实时决策博弈树

  • 问题:在有限时间内做出决策
  • 解法:迭代加深搜索
class RealTimeSolver {private:chrono::milliseconds time_limit;public:RealTimeSolver(int ms_limit) : time_limit(ms_limit) {}int iterativeDeepening(shared_ptr<GameTreeNode> root) {auto start_time = chrono::steady_clock::now();int depth = 1;int best_value = 0;MinimaxSolver solver;while (true) {auto current_time = chrono::steady_clock::now();auto elapsed = chrono::duration_cast<chrono::milliseconds>(current_time - start_time);if (elapsed > time_limit * 0.9) {  // 留10%安全边际break;}// 使用超时保护auto future = async(launch::async, [&]() {return solver.solve(root);});if (future.wait_for(time_limit - elapsed) == future_status::ready) {best_value = future.get();} else {break;  // 超时}depth++;}cout << "搜索深度: " << depth - 1 << endl;return best_value;}
};

总结

博弈树是博弈论和人工智能的核心概念:

  1. 理论基础:为序贯决策提供数学模型
  2. 算法框架:Minimax 和 Alpha-Beta 成为游戏 AI 的基础
  3. 扩展性:可处理多玩家、随机事件、不完全信息等复杂情况
  4. 实用性:在象棋、围棋、扑克等游戏中取得巨大成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/942478.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在一台机器上搭建一体化 Ceph 存储集群

概述 Ceph 是一个开源的软件定义存储平台,它在单个分布式计算机集群上实现对象存储,并提供对象级、块级和文件级存储的三合一接口。Ceph 存储集群由 Ceph 监视器、Ceph 管理器、Ceph 元数据服务器和 OSD 组成,它们协…

2025年硅锰合金厂家推荐排行榜,硅锰合金颗粒,硅锰合金粉,高纯度硅锰合金材料源头厂家深度解析

2025年硅锰合金厂家推荐排行榜,硅锰合金颗粒,硅锰合金粉,高纯度硅锰合金材料源头厂家深度解析 一、行业背景与发展趋势 硅锰合金作为钢铁冶炼过程中不可或缺的脱氧剂和合金添加剂,在冶金工业中占据重要地位。随着钢…

byte,short,int,Long,char数据类型复习

byte,short,int,Long,char数据类型复习byte, short, int, Long, char数据类型复习 package com.kun.base;/**byte的取值范围:-128~127(-2的7次方到2的7次方-1)short的取值范围:-32768~32767(-2的15次方到…

PyCharm下载安装教程及激活步骤(附安装包)超详细保姆级教程

目录一、PyCharm 2024 到底是啥?写 Python 为啥都用它?二、PyCharm 2024下载及安装准备2.1 电脑需满足这些条件2.2 下载PyCharm 2024三、PyCharm 2024 安装与激活步骤(详细分步)3.1 解压 PyCharm 2024 安装包3.2 解…

2025 年活性炭源头厂家最新推荐榜,技术实力与市场口碑深度解析,筛选优质可靠品牌颗粒/柱状/粉末/煤质/木质活性炭

引言 当前活性炭市场虽蓬勃发展,但乱象丛生,部分厂家用劣质原料降低成本,导致产品吸附性能不稳定,无法满足食品、医药等高端领域需求;落后生产工艺不仅效率低,还高碳排放,违背绿色发展趋势;品控缺失使产品关键…

2025年手持光谱仪厂家推荐排行榜,光谱分析仪,便携式光谱仪,矿石元素分析仪,合金金属不锈钢铝合金,贵金属三元催化检测设备公司精选

2025年手持光谱仪厂家推荐排行榜:光谱分析技术革新与选购指南 手持光谱仪作为现代工业检测领域的重要工具,在材料分析、质量控制和科研检测中发挥着不可替代的作用。随着技术的不断进步,2025年的手持光谱仪市场呈现…

Windows下利用 Python OCR 识别电子发票(增值税专用发票)(使用 GhostScript 和 Tesseract )

在早起Python公众号下,作者陈熹的解放双手|Python 批量自动提取、整理 PDF 发票!文章中,看到根据坐标识别图片的方法,觉得代码不是太详细。 试着在windows下重现,如下。 所需 requirements.txt 可以是 # Wand - …

2025年臭氧检测仪厂家权威推荐榜:在线式/固定式/便携式/手持式/工业臭氧检测仪专业选购指南

2025年臭氧检测仪厂家权威推荐榜:在线式/固定式/便携式/手持式/工业臭氧检测仪专业选购指南 一、行业技术发展现状与趋势 随着工业安全与环境保护意识的不断提升,臭氧检测仪作为关键的气体监测设备,在半导体制造、水…

✨WPF编程基础【2.2】:布局面板实战 - 详解

✨WPF编程基础【2.2】:布局面板实战 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &quo…

2025年拖鞋机厂家权威推荐榜:酒店拖鞋生产线,全自动拖鞋机,一次性拖鞋机,酒店一次性拖鞋机器专业选购指南

2025年拖鞋机厂家权威推荐榜:酒店拖鞋生产线,全自动拖鞋机,一次性拖鞋机,酒店一次性拖鞋机器专业选购指南 行业背景与发展趋势 随着全球酒店业的快速发展和卫生标准的不断提升,一次性拖鞋作为酒店必备用品,其生产…

2025年不锈钢酸洗钝化液厂家推荐排行榜:环保型不锈钢清洗钝化液,不锈钢管酸洗钝化处理,不锈钢清洗剂专业选购指南

2025年不锈钢酸洗钝化液厂家推荐排行榜:环保型不锈钢清洗钝化液,不锈钢管酸洗钝化处理,不锈钢清洗剂专业选购指南 行业背景与发展趋势 随着制造业转型升级和环保政策趋严,不锈钢表面处理行业正经历深刻变革。不锈钢…

达梦8加密函数是什么怎么调用,达梦数据库加密算法

达梦数据库透明加密方法,可以分为全库加密、表空间加密、日志加密。 全库加密和日志加密,只有在初始化实例的时候配置生效。 配置了全库加密,则所有表空间也是加密的,不允许表空间再单独加密。 DB用户使用加密表空…

电话呼叫软件网页版实测报告:体验、稳定性与推荐名单

在呼叫中心、客服外呼、销售跟进等业务场景中,电话呼叫软件已成为企业的“标配工具”。但近年来,随着企业信息化转型的深入,越来越多的团队开始从传统的本地安装系统,转向部署更轻量、更灵活的网页版电话呼叫软件。…

【2025最新】ArcGIS for JS 实现地图卷帘效果,动态修改参数(进阶版) - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

基于Windows,Docker用法

1、安装Docker Desktop2、打开Docker Desktop,查找Images来Pull安装node:latestnginx:latest3、挂载本地目录 docker run -it --rm -v F:/demo:/app -w /app -p 5173:5173 node:latest bash # -v:与 /app 相互映射# …

厨房电子秤方案:厨房秤常规的功能有那些?

厨房秤的常规功能围绕精准称重和便捷操作两大核心设计,覆盖从基础称重到辅助烹饪的多个场景,满足日常家用和轻度专业需求。这些功能看似基础,却能大幅提升烹饪的精准度和效率,尤其适合烘焙、减脂餐制作等对食材重量…

Pandas -

Pandas - import pandas as pd import numpy as npdf_tables = pd.read_csv(tables.csv, names=[table_name], header=None)df_tables[split_list] = df_tables[table_name].str.split(_)df_tables[layer] = np.where(…

A-Beta 剪枝

模型介绍 Alpha-Beta 剪枝是极小化极大算法(Minimax)的优化版本,用于两人零和博弈的决策树搜索。它通过剪枝来减少需要评估的节点数量,同时保证找到与 Minimax 算法相同的最优解。 核心思想:Alpha:MAX 玩家能保证…

MySQL 死锁 怎么处理?

一、什么是死锁(Deadlock) 定义:死锁是指两个或多个事务在执行过程中,互相占用资源且等待对方释放,导致事务都无法继续执行的状态。简单例子:事务A事务BUPDATE t1 SET ... WHERE id=1; UPDATE t1 SET ... WHERE …

MyBatis 的 @SelectProvider 是一个强大的注解,用于动态生成 SQL 语句

MyBatis 的 @SelectProvider 是一个强大的注解,用于动态生成 SQL 语句。让我详细介绍一下它的用途和使用方法。 一、@SelectProvider 的作用 主要用途:动态 SQL 构建 - 根据条件动态生成复杂的 SQL 代码逻辑控制 - 使…