日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【笔记】蒙特卡洛树搜素(MCTS)与三子棋

發布時間:2023/12/20 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【笔记】蒙特卡洛树搜素(MCTS)与三子棋 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

蒙特卡洛方法與蒙特卡洛樹

蒙特卡洛(Monter Carlo)是一座著名的賭城。既然是賭,那多多少少會帶有隨機的成分。借用其名的蒙特卡洛方法就是一種隨機方法。

在概率論的課堂上有過利用蒙特卡洛方法計算圓周率的例子(投針法):

  • 在地面上繪制等間距為L的平行直線
  • 用若干根長度為d(d < L)的針投向地面
  • 統計針與直線相交的頻率p
  • 計算圓周率 π = 2d / pL
  • 當d = 2L時,π = 1 / p。證明方法主要有兩種(課堂上用的是第二種):

  • 如果投的是一個周長為d的圓環,則其直徑應為:d / π,那么它與直線相交的概率為 d / πL,且相交時交點數為2(相切時因為是第一類間斷點對積分沒有影響;或可視為2個重合的交點)。由此,交點數的均值就是2d / πL。將圓環掰直以后,其實它們與直線相交的機會還是均等的,只不過此時交點最多為1,且概率P = 2d / πL,用頻率逼近概率可以得到π = 2d / pL。
  • 當然也可以用純數學的公式推導。設針的中點到離它最近的一條直線的距離為a, 針與直線的夾角為θ,則a服從[0, L / 2]的隨機分布,θ服從[0,π / 2]的隨機分布,f(x) = 4 / πL。當針與直線相交時,a∈[0,d sinθ / 2],對a、θ積分可得相同結果。
  • 當然用蒙特卡羅方法計算圓周率還有更加直觀的方法,比如統計大量隨機點落入圓形的概率,結合圓形的面積公式計算圓周率。

    與這種方法類似,蒙特卡羅樹搜索就是用大量的、隨機的搜索次數尋找最佳的選擇;但蒙特卡洛樹搜素的方法并非完全隨機——在利用置信區間選擇出最佳節點之后,隨機進行模擬而找到葉節點。利用置信區間選擇時會用到相關的啟發函數,因此,蒙特卡洛樹搜素可以視為一種啟發式算法。

    基本博弈理論

    完全信息博弈

    完全信息博弈指博弈雙方信息完全共享的博弈,其中,動態博弈是指博弈雙方輪流決策,靜態博弈是指博弈雙方同時決策。

    組合博弈

    組合博弈是指滿足以下條件的博弈:

  • 有且僅有兩個玩家
  • 游戲雙方輪流操作
  • 游戲操作狀態是個有限的集合
  • 游戲必須在有限次內結束
  • 當一方無法操作時,游戲結束。
  • 零和博弈

    若記博弈中勝利為1,平局為0,失敗為-1,那么在零和博弈中博弈雙方的總收益之和為零;換言之,在零和博弈中沒有雙贏和雙輸。

    蒙特卡洛樹搜素適用條件

  • 完全信息博弈
  • 零和博弈
  • 搜索空間巨大(很難實現更高層次的窮舉,如圍棋等)
  • 組合游戲
  • 每一個操作沒有隨機因素且沒有連續值
  • 置信區間上界(Upper Confidence Bound,UCB)

    這里采用UCB1策略衡量節點探索的價值:
    V ̄i+2lnNni\overline V_i+ \sqrt \frac{2lnN}{n_i}Vi?+ni?2lnN??
    其中,V ̄i\overline V_iVi?表示當前節點的平均價值;
    NNN表示總的探索次數;
    nin_ini?表示當前節點的探索次數。

    基本步驟

    蒙特卡洛樹搜索有四個基本步驟:選擇、擴展、模擬、反向傳播。

    選擇(Selection)

    若該節點已經是葉子節點(博弈結束),則直接進入反向傳播;否則,選擇當前節點下UBC值最大的、未被完全擴展的子節點,若UCB值最大的節點已經被擴展,需要進一步選擇其UCB值最大的子節點,直到找到未被完全擴展的節點。

    擴展(Expansion)

    從選擇的節點出發,隨機選擇一個合法動作創建子節點(或多個子節點,再從中隨機選一個)。

    仿真(Simulation)

    順著這個動作,隨機地模擬游戲,直到游戲結束,判斷模擬的結果,勝記為1,負記為-1,平局記為0。

    反向傳播(Backpropagation)

    將仿真的結果從葉子節點向上更新,所經過的節點的值加上該葉子節點的值(勝負結果),探索次數增加1.

    利用蒙特卡洛樹搜素實現三子棋

    簡單地用C語言實現了一下,但效果并不好……可能是因為隨機數或搜索次數太少了……

    創建棋局

    #include<stdlib.h> #include<stdio.h> #include<math.h> #include<time.h> #define Inf 99999 #define TIME 3000 //搜索次數 #define C 1 //電腦走子命令 #define P 0 //玩家走子命令char map[3][3] = {0}; //當前棋局 int T = 9, N = 0; //T是還可以走的空格數,N是總的訪問次數typedef struct NODE {int t; //未被填滿的空格數int loc; //loc=x+y*10; loc%10=x; loc/10=ychar map[3][3];char vis[3][3];float val;int order; //該步操作的命令方,取值為P或Cint N; //該節點的探索次數struct NODE *sib;struct NODE *fth;struct NODE *kid; }Node, *Node_ptr;Node_ptr new(Node_ptr ptr) { //從ptr新建葉子節點并初始化Node_ptr p = (Node_ptr)malloc(sizeof(Node));p->loc = -1;p->t = 0;int i, j;for (i = 0; i < 3; i++)for (j = 0; j < 3; j++){if(ptr){p->map[i][j] = ptr->map[i][j];if (ptr->map[i][j] != ' ') {p->vis[i][j] = 1;p->t++;}elsep->vis[i][j] = 0;}if(!ptr){p->map[i][j] = map[i][j];p->vis[i][j] = 0;if(map[i][j] != ' '){p->vis[i][j] = 1;p->t++;}}}p->val = 0;p->N = 0;p->sib = NULL;if (ptr) {p->sib = ptr->kid;ptr->kid = p;}if (ptr == NULL || ptr->order == P)p->order = C;elsep->order = P;p->fth = ptr;p->kid = NULL;return p; }int win(int x, int y, char map[3][3]) { //判斷輸贏if (map[x][0] == map[x][1] && map[x][1] == map[x][2])return 1;if (map[0][y] == map[1][y] && map[1][y] == map[2][y])return 1;if (x - y == 0 && (map[0][0] == map[1][1] && map[1][1] == map[2][2])) return 1;if (x + y == 2 && map[0][2] == map[1][1] && map[1][1] == map[2][0])return 1;return 0; }void print(char map[3][3]) { //打印棋盤int i = 0;printf(" --- --- ---\n");for (i = 0; i < 3;i++)printf(" | %c | %c | %c |\n --- --- ---\n", map[i][0], map[i][1], map[i][2]); }

    接下來定義電腦和玩家的走子

    void computer_MCTS(){int t = TIME; //重置需要搜索的次數限制N = 0; //重置總訪問次數Node_ptr p = new (NULL), root, sol;while(t--) {root = selection(p);root = expansion(root);rollout(root);N++;}root = p->kid;sol = p->kid;while(root){if (root->N > sol->N)sol = root;root = root->sib;}int x, y;x = sol->loc % 10;y = sol->loc / 10;map[x][y] = '-';printf("Computer's step: <%d, %d>\n", x + 1, y + 1);print(map);Dis(p);if (win(x, y, map)) printf("Sorry, you lost!\n");else if(!--T){printf("Draw!\n");return;}else playerplay(); }void computerplay(){computer_MCTS(); }void playerplay(){int x, y;printf("Your step: ");scanf("%d%d", &x, &y);while (1){if(x < 1 || x > 3 || y < 1 || y > 3){printf("Illegal input!\n");scanf("%d%d", &x, &y);}else if (map[x - 1][y - 1]!=' ') {printf("Already existed!\n");scanf("%d%d", &x, &y);}elsebreak;}map[x - 1][y - 1] = '+';print(map);if (win(x - 1, y - 1, map))printf("Congratulations, you win!\n");else if(!--T){printf("Draw!\n");return;}else computerplay(); }

    選擇

    float UCB(Node_ptr p) { //計算UCB值if (p->N == 0)return Inf;elsereturn p->val / p->N + sqrt(2 * N / p->N); }Node_ptr selection(Node_ptr p){int i, j;//判斷是否為葉子節點,若是返回當前節點進入反向傳播for (i = 0; i < 3; i++)for (j = 0; j < 3; j++)if (p->vis[i][j] != 0)return p;int max = -Inf;Node_ptr M;Node_ptr m = p;while(m){M = m;while (m) {if (UCB(m) > max) {max = UCB(m);M = m;}m = m->sib;}m = M->kid;}return M; }

    擴展

    Node_ptr expansion(Node_ptr p) {int i, j;Node_ptr n, g;for (i = 0; i < 3; i++)for (j = 0; j < 3; j++) {if (p->map[i][j] != ' ' || p->vis[i][j])continue;n = new (p);if (n->order == P)n->map[i][j] = '-';elsen->map[i][j] = '+';n->vis[i][j] = 1;n->t++;n->fth = p;n->loc = i + j * 10;if(p->vis[i][j] == 0)p->vis[i][j] = 1;}int r = rand() % T;g = p->kid;while(r--) {if (g == NULL)g = p->kid;else g = g->sib;}if (g == NULL)g = p->kid;return g; }

    仿真

    定義隨機走子的操作

    Node_ptr playR(int order, Node_ptr p, int t) {if(!t){p->val = 0;return p;}Node_ptr q = new (p);//隨機走子int r = rand() % t;int x, y;x = r / 3;y = r % 3;while (q->map[x][y] != ' ') {x = (++r / 3) % 3;y = r % 3;}p->vis[x][y] = 1;q->loc = x + y * 10;if(order == C){q->map[x][y] = '-';if (win(x, y, q->map)) {q->val = 1;return q;}else return playR(P, q, t - 1);}else if(order == P) {q->map[x][y] = '+';if (win(x, y, q->map)) {q->val = -1;return q;}else return playR(C, q, t-1);} }

    定義仿真

    void rollout(Node_ptr p) {Node_ptr ptr = playR(p->order, p, 9 - p->t);backpropagation(ptr->fth, ptr->val); }

    反向傳播

    void backpropagation(Node_ptr p, int order) {if (p == NULL)return;else {backpropagation(p->fth, order);p->val += order;p->N++;} }

    主函數

    int main ( ) {int i, j;char order;srand((unsigned)time(NULL));for (i = 0; i < 3; i++)for (j = 0; j < 3; j++)map[i][j] = ' ';printf("Do you wanna play first? y/n\n");scanf("%c", &order);getchar();if (order == 'y') {playerplay();}else if (order == 'n') {computerplay();}return 0; }

    結果的選擇

    完成蒙特卡羅樹搜索后該如何作出最優決策呢?主要有三種選擇:

  • 選擇UCB值最大的節點
  • 選擇訪問次數最多的節點
  • 選擇勝率最高的節點
  • 一般會采用訪問次數最多的節點作為最終的決策。

    總結

    以上是生活随笔為你收集整理的【笔记】蒙特卡洛树搜素(MCTS)与三子棋的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。