蒙特卡洛方法原理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蒙特卡洛方法原理相关的知识,希望对你有一定的参考价值。
蒙特卡罗法也称统计模拟法、统计试验法。是把概率现象作为研究对象的数值模拟方法。是按抽样调查法求取统计值来推定未知特性量的计算方法。蒙特卡罗是摩纳哥的著名赌城,该法为表明其随机抽样的本质而命名。故适用于对离散系统进行计算仿真试验。在计算仿真中,通过构造一个和系统性能相近似的概率模型,并在数字计算机上进行随机试验,可以模拟系统的随机特性蒙特卡洛法(又称统计试验法)是描述装备运用过程中各种随机现象的基本方法,而且它特别适用于一些解析法难以求解甚至不可能求解的问题,因而在装备效能评估中具有重要地位。
用蒙特卡洛法来描述装备运用过程是1950年美国人约翰逊首先提出的。这种方法能充分体现随机因素对装备运用过程的影响和作用。更确切地反映运用活动的动态过程。在装备效能评估中,常用蒙特卡洛法来确定含有随机因素的效率指标,如发现概率、命中概率、平均毁伤目标数等;模拟随机服务系统中的随机现象并计算其数字特征;对一些复杂的装备运用行动,通过合理的分解,将其简化成一系列前后相连的事件,再对每一事件用随机抽样方法进行模拟,最后达到模拟装备运用活动或运用过程的目的。[2]
基本思路
蒙特卡洛法的基本思想是:为了求解问题,首先建立一个概率模型或随机过程,使它的参数或数字特征等于问题的解:然后通过对模型或过程的观察或抽样试验来计算这些参数或数字特征,最后给出所求解的近似值。解的精确度用估计值的标准误差来表示。蒙特卡洛法的主要理论基础是概率统计理论,主要手段是随机抽样、统计试验。用蒙特卡洛法求解实际问题的基本步骤为:
(1)根据实际问题的特点.构造简单而又便于实现的概率统计模型.使所求的解恰好是所求问题的概率分布或数学期望;
(2)给出模型中各种不同分布随机变量的抽样方法;
(3)统计处理模拟结果,给出问题解的统计估计值和精度估计值。[2]
优缺点
蒙特卡罗法的最大优点是:
1.方法的误差与问题的维数无关。
2.对于具有统计性质问题可以直接进行解决。
3.对于连续性的问题不必进行离散化处理
蒙特卡罗法的缺点则是:
1.对于确定性问题需要转化成随机性问题。
2.误差是概率误差。
3.通常需要较多的计算步数N.
蒙特卡罗法作为一种计算方法,是由美国数学家乌拉姆(Ulam , S. M.)与美籍匈牙利数学家冯·诺伊曼(von Neumann,J.)在20世纪40年代中叶,为研制核武器的需要而首先提出来的.实际上,该方法的基本思想早就被统计学家所采用了.例如,早在17世纪,人们就知道了依频数决定概率的方法。
步骤
蒙特卡洛法是一种用来模拟随机现象的数学方法,这种方法在作战模拟中能直接反映作战过程中的随机性。在作战模拟中能用解析法解决的问题虽然越来越多,但有些情况下却只能采用蒙特卡洛法。使用蒙特卡洛法的基本步骤如下:
(1)根据作战过程的特点构造模拟模型;
(2)确定所需要的各项基础数据;
(3)使用可提高模拟精度和收敛速度的方法;
(4)估计模拟次数;
(5)编制程序并在计算机上运行;
(6)统计处理数据,给出问题的模拟结果及其精度估计。
在蒙特卡洛法中,对同一个问题或现象可采用多种不同的模拟方法,它们有好有差,精度有高有低,计算量有大有小,收敛速度有快有慢,在方法的选择上有一定的技巧。[3]
应用举例
在我方某前沿防守地域,敌人以1个炮兵排(含两门火炮)为单位对我方进行干扰和破坏。为躲避我方打击,敌方对其指挥所进行了伪装并经常变换射击地点。经过长期观察发现,我方指挥所对敌方目标的指示有50%是准确的,而我方火力单位在指示正确时,有1/3的射击效果能毁伤敌人1门火炮,有1/6的射击效果能全部消灭敌人。
解:希望能用某种方法把我方将要对敌人实施的20次打击结果显示出来,确定有效射击的比率及毁伤敌方火炮的平均值。这是一个概率问题,可以通过理论计算得到相应的概率和期望值。但这样只能给出作战行动的最终静态结果,而显示不出作战行动的动态过程。
为了显示我方20次射击的过程,必须用某种方式模拟出以下两件事:一是观察所对目标的指示正确或不正确;二是当指示正确时,我方火力单位的射击结果。对第一件事进行模拟试验时有两种结果,每一种结果出现的概率都是1/2。因此,可用投掷1枚硬币的方式予以确定。当硬币出现正面时为指示正确,反之为不正确。对第二件事进行模拟试验时有3种结果,毁伤1门火炮的可能为1/3,毁伤2门火炮的可能为1/6,没能毁伤敌火炮的可能为1/2。这时,可用投掷骰子的办法来确定,如果出现的是1、2、3三个点则认为没能击中敌人,如果出现的是4、5点则认为毁伤敌1门火炮,如果出现6点则认为毁伤敌2门火炮。
通过上面的方式,就可把我方20次射击的过程动态地显现出来。 参考技术A 蒙特卡洛方法(Monte Carlo method),也称统计模拟方法,是二十世纪四十年代中期由于科学技术的发展和电子计算机的发明,而被提出的一种以概率统计理论为指导的一类非常重要的数值计算方法。它是以概率统计理论为基础, 依据大数定律( 样本均值代替总体均值) , 利用电子计算机数字模拟技术,解决一些很难直接用数学运算求解或用其他方法不能解决的复杂问题的一种近似计算法。蒙特卡洛方法在金融工程学,宏观经济学,计算物理学(如粒子输运计算、量子热力学计算、空气动力学计算)等领域应用广泛。
其基本原理如下:由概率定义知,某事件的概率可以用大量试验中该事件发生的频率来估算,当样本容量足够大时,可以认为该事件的发生频率即为其概率。因此,可以先对影响其可靠度的随机变量进行大量的随机抽样,然后把这些抽样值一组一组地代入功能函数式,确定结构是否失效,最后从中求得结构的失效概率。蒙特卡洛法正是基于此思路进行分析的。
设有统计独立的随机变量Xi(i=1,2,3,„,k),其对应的概率密度函数分别为fx1,fx2,„,fxk,功能函数式为Z=g(x1,x2,„,xk)。首先根据各随机变量的相应分布,产生N组随机数x1,x2,„,xk值,计算功能函数值Zi=g(x1,x2,„,xk)(i=1,2,„,N),若其中有L组随机数对应的功能函数值Zi≤0,则当N∞时,根据伯努利大数定理及正态随机变量的特性有:结构失效概率,可靠指标。 参考技术B 蒙特卡罗方法,是一种统计实验或随机抽样的方法,属于计算数学的分支。该方法从物理系统背后的数学模型出发,引入随机数和概率统计方法,进行一定次数的模拟计算得出计算结果。 参考技术C 概率论 统计学方法
深度解析黑白棋AI代码原理(蒙特卡洛搜索树MCTS+Roxanne策略)
深度解析黑白棋AI代码原理(蒙特卡洛搜索树MCTS+Roxanne策略)
文章目录
黑白棋规则
黑白棋是通过相互翻转对方的棋子,最后看棋盘上谁的棋子多来判定胜负的游戏。
黑白棋棋盘共有8行8列共64格
开局时,棋盘正中央的4洛先置放黑自相隔的
4枚棋子
通常黑子先行,双方轮流落子
只要落子和棋盘上任意一枚己方的棋子在一条直线上(横,直,斜线皆可)夹着对方棋
就能将对方的这些棋子转变为己方
如果在任意位置落子都不能夹住对方的任意一颗棋子,就要让对手下子。
当双放皆不能下子时,游戏结束,子多的一方胜利
传统黑白棋策略
传统黑白棋策略包括以下几类:
- a) 贪心策略。
每一步走子都选择使得棋盘上子最多的一步,而不考虑最终的胜负; - b) 确定子策略。
某些子一旦落子后就再也不会被翻回对方的子,最典型的是四个角上的子,这类子被称为确定子(Stable Discs)。每一步走子都选择使得棋盘上己方的确定子最多的一部。 - c) 位置优先策略。
考虑到角点的重要性,把棋盘上的每一个子都赋予一个优先级,每一步从可走子里选择优先级最高的一个子。 - d) 机动性策略(mobility)。
黑白棋每一步的可走子都是有限的,机动性策略是指走子使得对手的可走子较少,从而逼迫对手不得不走出差的一步(bad move),使得自己占据先机。 - e) 消失策略(evaporation, less is more)。
在棋盘比试的前期,己方的子越少往往意味着局势更优。因此在前期可采用使己方的子更少的走子。 - f) 奇偶策略(parity)。
走子尽量走在一行或一列有奇数个空子的位置。 以上只列举了一些常见的黑白棋策略或原则,事实上还有很多更为复杂的策略,此处不进行列举。
蒙特卡洛搜索树
基本概念
蒙特卡洛树搜索(简称 MCTS)是 Rémi Coulom 在 2006 年在它的围棋人机对战引擎 「Crazy Stone」中首次发明并使用的的 ,并且取得了很好的效果。
我们先讲讲它用的原始 MCTS 算法(ALphago 有部分改进)
蒙特卡洛树搜索,首先它肯定是棵搜索树
我们回想一下我们下棋时的思维——并没有在脑海里面把所有可能列出来,而是根据「棋感」在脑海里大致筛选出了几种「最可能」的走法,然后再想走了这几种走法之后对手「最可能」的走法,然后再想自己接下来「最可能」的走法。这其实就是 MCTS 算法的设计思路。
上面这段很重要,可以反复品味
它经历下面 3 个过程(重复千千万万次)
- 选择(Selection):
从根结点R开始,选择连续的子结点向下至叶子结点L。下面的结点有更多选择子结点的方法,使游戏树向最优点扩展移动,这是蒙特卡洛树搜索的本质。 - 扩展 (expansion):
除非任意一方的输赢导致游戏结束,否则L会创建一个或多个子结点或从结点C中选择。 - 模拟/仿真(Simluation):在结点C中进行随机布局。
- 回溯/反向传播(Backpropagation):使用布局结果更新从C到R的路径上的结点信息。
模拟(Simluation)
我们不按顺序,先讲模拟,模拟借鉴了我们上面说的蒙特卡洛方法,快速走子,只走一盘,分出个胜负。
我们每个节点(每个节点代表每个不同的局面)都有两个值,代表这个节点以及它的子节点模拟的次数和赢的次数,比如模拟了 10 次,赢了 4 盘,记为 4/10。
我们再看多一次这幅图,如图,每个节点都会标上这两个值。
选择(Selection)
我们将节点分成三类:
未访问:还没有评估过当前局面
未完全展开:被评估过至少一次,但是子节点(下一步的局面)没有被全部访问过,可以进一步扩展
完全展开:子节点被全部访问过
我们找到目前认为「最有可能会走到的」一个未被评估的局面(双方都很聪明的情况下),并且选择它。
什么节点最有可能走到呢?最直观的想法是直接看节点的胜率(赢的次数/访问次数),哪个节点最大选择哪个,但是这样是不行的!因为如果一开始在某个节点进行模拟的时候,尽管这个节点不怎么好,但是一开始随机走子的时候赢了一盘,就会一直走这个节点了。
因此人们造了一个函数
Q(v) 是该节点赢的次数,N(v) 是该节点模拟的次数,C 是一个常数。
因此我们每次选择的过程如下——从根节点出发,遵循最大最小原则,每次选择己方 UCT 值最优的一个节点,向下搜索,直到找到一个「未完全展开的节点」,根据我们上面的定义,未完全展开的节点一定有未访问的子节点,随便选一个进行扩展。
这个公式虽然我们造不出来,但是我们可以观赏它的巧妙之处,首先加号的前面部分就是我们刚刚说的胜率,然后加号的后面部分函数长这样:
随着访问次数的增加,加号后面的值越来越小,因此我们的选择会更加倾向于选择那些还没怎么被统计过的节点,避免了我们刚刚说的蒙特卡洛树搜索会碰到的陷阱——一开始走了歪路。
扩展(expansion)
将刚刚选择的节点加上一个统计信息为「0/0」的节点,然后进入下一步模拟(Simluation)
回溯(Backpropagation)
Backpropagation 很多资料翻译成反向传播,不过我觉得其实极其类似于递归里的回溯,就是从子节点开始,沿着刚刚向下的路径往回走,沿途更新各个父节点的统计信息。
再放一次这个图,可以观察一下在模拟过后,新的 0/0 节点,比如这里模拟输了,变成了 0/1,然后它的到根节点上的节点的统计信息的访问次数全部加 1,赢的次数不变。
算法什么时候可以终止
取决于你什么时候想让他停止,比如说你可以设定一个时间,比如五秒后停止计算。
一般来说最佳走法就是具有最高访问次数的节点,这点可能稍微有点反直觉。这样评估的原因是因为蒙特卡洛树搜索算法的核心就是,越优秀的节点,越有可能走,反过来就是,走得越多的节点,越优秀。
蒙特卡洛黑白棋
经过策略的实践发现,单一的贪心策略和消失策略效果并不理想,确定子策略的算法比较复杂,程序效率较低,而机动性策略难以用程序体现,奇偶策略的效果也不佳,而位置优先策略在UCT算法中表现优异。 程序中采用两种优先级策略。
第一种优先级如下图:
这个优先级表的首要策略是占据角点,所有优先级的设置都为此服务。
第二种优先级如下图:
这个优先级表由Roxanne提出,结合了多种策略,同时也结合了Mobility的特性,因为中间子的优先级较高,会提高自己的Mobility而限制对手的可走步数。
Roxanne 策略 详见 《Analysis of Monte Carlo Techniques in Othello》
提出者:Canosa, R. Roxanne canosa homepage. https://www.cs.rit.edu/~rlc/
代码:
# -*- coding = utf-8 -*-
# @Time : 2021/4/1 20:06
# @Author : Leokadia
# @File : Chess.py
# @Software : PyCharm
'''
a) 选择(Selection):从根结点R开始,选择连续的子结点向下至叶子结点L。
下面的结点有更多选择子结点的方法,使游戏树向最优点扩展移动,这是蒙特卡洛树搜索的本质。
b) 扩展(Expansion):除非任意一方的输赢导致游戏结束,否则L会创建一个或多个子结点或从结点C中选择。
c) 仿真(Simulation):在结点C中进行随机布局。
d) 反向传播(Backup):使用布局结果更新从C到R的路径上的结点信息。
'''
from func_timeout import func_timeout, FunctionTimedOut
import datetime # datetime模块用于操作时间
import random # random模块用于生成随机数
from math import log, sqrt # math数学计算库导入对数函数log,开方函数sqrt
from time import time # 时间模块用于时间的设置
#
from copy import deepcopy # 从copy模块导入深度拷贝方法
# 棋盘类
class ReversiBoard(object):
def __init__(self):
"""
初始化棋盘,棋盘大小为8*8,黑棋用 X 表示,白棋用 O 表示,未落子时用 . 表示
"""
self.board_init()
def board_init(self):
"""
重置棋盘
"""
self.empty = '.' # 未落子状态
self._board = [[self.empty for _ in range(8)] for _ in range(8)] # 规格:8*8,构造8*8个.
self._board[3][4], self._board[4][3] = 'X', 'X' # 黑棋棋子初始状态
self._board[3][3], self._board[4][4] = 'O', 'O' # 白棋棋子初始状态
# 棋局开始时黑棋位于E4和D5,白棋位于D4和E5
def display(self, step_time=None, total_time=None):
"""
打印棋盘
:param step_time: 每一步的耗时, 比如:"X":1,"O":0,默认值是None
:param total_time: 总耗时, 比如:"X":1,"O":0,默认值是None
:return:
"""
board = self._board
print(' ', ' '.join(list('ABCDEFGH'))) # 打印列名,开头空一格
for i in range(8): # 打印行名和棋盘
# print(board)
print(str(i + 1), ' '.join(board[i]))
''' Display time. '''
if (not step_time) or (not total_time): # 如果step_time=None, total_time=None
# 棋盘初始化时展示的时间
step_time = "X": 0, "O": 0
total_time = "X": 0, "O": 0
print("统计棋局: 棋子总数 / 每一步耗时 / 总时间 ")
print("黑 棋: " + str(self.count('X')) + ' / ' + str(step_time['X']) + ' / ' + str(total_time['X']))
print("白 棋: " + str(self.count('O')) + ' / ' + str(step_time['O']) + ' / ' + str(total_time['O']) + '\\n')
else:
# 比赛时展示时间
print("统计棋局: 棋子总数 / 每一步耗时 / 总时间 ")
print("黑 棋: " + str(self.count('X')) + ' / ' + str(step_time['X']) + ' / ' + str(total_time['X']))
print("白 棋: " + str(self.count('O')) + ' / ' + str(step_time['O']) + ' / ' + str(total_time['O']) + '\\n')
def count(self, color):
"""
统计 color 一方棋子的数量。(O:白棋, X:黑棋, .:未落子状态)
:param color: [O,X,.] 表示棋盘上不同的棋子
:return: 返回 color 棋子在棋盘上的总数
"""
count = 0
for y in range(8):
for x in range(8):
if self._board[x][y] == color:
count += 1
return count
def get_winner(self):
"""
判断黑棋和白旗的输赢,通过棋子的个数进行判断
:return: 0-黑棋赢,1-白旗赢,2-表示平局,黑棋个数和白旗个数相等
"""
# 定义黑白棋子初始的个数
black_count, white_count = 0, 0
for i in range(8):
for j in range(8):
# 统计黑棋棋子的个数
if self._board[i][j] == 'X':
black_count += 1
# 统计白旗棋子的个数
if self._board[i][j] == 'O':
white_count += 1
if black_count > white_count:
# 黑棋胜
return 0, black_count - white_count
elif black_count < white_count:
# 白棋胜
return 1, white_count - black_count
elif black_count == white_count:
# 表示平局,黑棋个数和白旗个数相等
return 2, 0
def _move(self, action, color):
"""
落子并获取反转棋子的坐标
:param action: 落子的坐标 可以是 D3 也可以是(2,3)
:param color: [O,X,.] 表示棋盘上不同的棋子
:return: 返回反转棋子的坐标列表,落子失败则返回False
"""
# 判断action 是不是字符串,如果是则转化为数字坐标
if isinstance(action, str):
action = self.board_num(action)
fliped = self._can_fliped(action, color)
if fliped:
# 有就反转对方棋子坐标
for flip in fliped:
x, y = self.board_num(flip)
self._board[x][y] = color # 将待翻转列表全部变成自己的color
# 落子坐标
x, y = action # 落子坐标改为我要下的地方坐标
# 更改棋盘上 action 坐标处的状态,修改之后该位置属于 color[X,O,.]等三状态
self._board[x][y] = color
return fliped
else:
# 没有反转子则落子失败
return False
# 从子节点开始,沿着刚刚向下的路径往回走
# 沿途更新各个父节点的统计信息。
def backpropagation(self, action, flipped_pos, color):
"""
回溯
:param action: 落子点的坐标
:param flipped_pos: 反转棋子坐标列表
:param color: 棋子的属性,[X,0,.]三种情况
:return:
"""
# 判断action 是不是字符串,如果是则转化为数字坐标
if isinstance(action, str):
action = self.board_num(action)
# 还原棋盘
self._board[action[0]][action[1]] = self.empty
# 如果 color == 'X',则 op_color = 'O';否则 op_color = 'X'
op_color = "O" if color == "X" else "X"
for p in flipped_pos:
# 判断action 是不是字符串,如果是则转化为数字坐标
if isinstance(p, str):
p = self.board_num(p)
self._board[p[0]][p[1]] = op_color
def is_on_board(self, x, y):
"""
判断坐标是否出界
:param x: row 行坐标
:param y: col 列坐标
:return: True or False
"""
return x >= 0 and x <= 7 and y >= 0 and y <= 7
def _can_fliped(self, action, color):
"""
检测落子是否合法,如果不合法,返回 False,否则返回反转子的坐标列表
:param action: 下子位置
:param color: [X,0,.] 棋子状态
:return: False or 反转对方棋子的坐标列表
"""
# 判断action 是不是字符串,如果是则转化为数字坐标
if isinstance(action, str):
action = self.board_num(action)
xstart, ystart = action
# 如果该位置已经有棋子或者出界,返回 False
if not self.is_on_board(xstart, ystart) or self._board[xstart][ystart] != self.empty:
return False
# 临时将color放到指定位置
self._board[xstart][ystart] = color
# 棋手
op_color = "O" if color == "X" else "X"
# 要被翻转的棋子
flipped_pos = [] # 数字坐标
flipped_pos_board = [] # 棋盘坐标
for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]: #八个方向依次遍历
x, y = xstart, ystart
x += xdirection
y += ydirection
# 如果(x,y)在棋盘上,而且为对方棋子,则在这个方向上继续前进,否则循环下一个角度。
if self.is_on_board(x, y) and self._board[x][y] == op_color:
x += xdirection
y += ydirection
# 进一步判断点(x,y)是否在棋盘上,如果不在棋盘上,继续循环下一个角度,如果在棋盘上,则进行while循环。
if not self.is_on_board(x, y):
continue
# 一直走到出界或不是对方棋子的位置
while self._board[x][y] == op_color:
# 如果一直是对方的棋子,则点(x,y)一直循环,直至点(x,y)出界或者不是对方的棋子。
x += xdirection
y += ydirection
# 点(x,y)出界了和不是对方棋子
if not self.is_on_board(x, y):
break
# 出界了,则没有棋子要翻转OXXXXX
if not self.is_on_board(x, y):
continue
# 是自己的棋子OXXXXXXO
if self._board[x][y] == color:
while True:
x -= xdirection
y -= ydirection
# 回到了起点则结束
if x == xstart and y == ystart:
break
# 需要翻转的棋子
flipped_pos.append([x, y])
# 将前面临时放上的棋子去掉,即还原棋盘(可以下的棋子已经在要翻转的列表中了)
self._board[xstart][ystart] = self.empty # restore the empty space
# 没有要被翻转的棋子,则走法非法。返回 False
if len(flipped_pos) == 0:
return False
for fp in flipped_pos:
flipped_pos_board.append(self.num_board(fp))
# 走法正常,返回翻转棋子的棋盘坐标
return flipped_pos_board
def get_legal_actions(self, color): #我的字的颜色
"""
按照黑白棋的规则获取棋子的合法走法
:param color: 不同颜色的棋子,X-黑棋,O-白棋
:return: 生成合法的落子坐标,用list()方法可以获取所有的合法坐标
"""
# 表示棋盘坐标点的8个不同方向坐标,比如方向坐标[0][1]则表示坐标点的正上方。
direction = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)]
op_color = "O" if color == "X" else "X"
# 统计 op_color 一方邻近的未落子状态的位置
op_color_near_points = []
board = self._board
for i in range(8):
# i 是行数,从0开始,j是列数,也是从0开始
for j in range(8):
# 判断棋盘[i][j]位子棋子的属性,如果是op_color,则继续进行下一步操作,
# 否则继续循环获取下一个坐标棋子的属性
if board[i][j] == op_color:
# dx,dy 分别表示[i][j]坐标在行、列方向上的步长,direction 表示方向坐标
for dx, dy in direction:
x, y = i + dx, j + dy
# 表示x、y坐标值在合理范围,棋盘坐标点board[x][y]为未落子状态,
# 而且(x,y)不在op_color_near_points 中,统计对方未落子状态位置的列表才可以添加该坐标点
if 0 <= x <= 7 and 0 <= y <= 7 and board[x][y] == self.empty and (
x, y) not in op_color_near_points:
op_color_near_points.append((x, y))
l = [0, 1, 2, 3, 4, 5, 6, 7]
for p in op_color_near_points:
# 判断落位是否合法,合法则进行下一步
if self._can_fliped(p, color):
# 判断p是不是数字坐标,如果是则返回棋盘坐标
# p = self.board_num(p)
if p[0] in l and p[1] in l: #判断p是不是数字坐标,如果是则返回棋盘坐标
p = self.num_board(p)
yield p
def board_num(self, action):
"""
棋盘坐标转化为数字坐标
:param action:棋盘坐标,比如A1
:return:数字坐标,比如 A1 --->(0,0)
"""
row, col = str(action[1]).upper(),以上是关于蒙特卡洛方法原理的主要内容,如果未能解决你的问题,请参考以下文章
ML-17-1MCMC--蒙特卡罗方法(Monte Carlo)