1.實驗問題
在4x4矩陣中添加終點和障礙點,分別有一個或多個,並且滿足以下屬性:
終點:value值不變,始終為0,鄰接點可到達用大寫字母E表示
障礙點:表示該點在矩陣中“不存在”,鄰接點不可到達該點,且該點沒有value值跟狀態,使用符號‘#’表示
以任意除以上兩種結點之外的所有其它結點為起點,求解起點到終點的最短距離,存在多終點時,以相隔最近的終結點為准。
2.實驗思路
1) 使用值Policy Iteration和Value Iteration算法分別計算問題產生的最佳策略
2) 設置獎勵值r=-1,折扣值disc = 0.57f,使用新舊兩個狀態矩陣和兩個Value矩陣用於迭代過程中保存狀態變化和價值變化。
3) MDP模型描述:
S: 狀態集合為{0…15},由4x4的矩陣S存儲,狀態與狀態之間滿足馬爾可夫屬性,因為當前狀態可到達的狀態最多四個,后繼狀態只受當前狀態影響。
A:可采取的action集合,使用A={n,e,s,w},北東南西表示
P:狀態轉移概率矩陣:不在編程中顯式出現,因為迭代時已知繼承狀態的value值
R : 因為求解最短距離,agent每行動一步可獲的獎勵為r=-1
γ : 折扣因子設置為 0.57,在0~1之間可調,但不能等於0或1,否則會造成無法收斂,無限循環的問題。
4) 值迭代:
a.初始化每個狀態的value值為V(s) = 0;
b.然后最大化每個狀態當前的value值,包括行動獎勵和未來獎勵,直到value值變化不明顯為止
c.根據以上步驟求得的收斂值矩陣V,對每個狀態遍歷查找使得總獎勵(行動獎勵和未來獎勵)最大的action,放入策略矩陣S,並打印輸出。
5)策略迭代:
a.初始化V矩陣為0,狀態矩陣中的每個狀態隨機選擇四個方向之一進行初始化,並且在狀態矩陣中添加障礙點’#’和終結點‘E’
b.根據選擇的策略,最優化V矩陣
c.優化當前策略:如果當前策略並不使得當前狀態的value值最大,那么把當前策略替換為最優策略
d如果對於所有狀態,所有策略均未發生改變,那么輸出當前的最佳策略,否則繼續從b開始循環
3.實驗結果
1)策略迭代
不同障礙及不同終節點下的收斂情況,其中 ‘#’表示障礙,‘E’表示終結點
2 )值迭代
不同障礙和終結點,折扣值為1.0
4.實驗代碼;
1)策略迭代:
#include <iostream> #include <algorithm> #include <cstdio> #include <set> #include <queue> #include <windows.h> #include <string> using namespace std; const int MAX_N = 4; const float INF = -999999999.0f; float V[MAX_N][MAX_N]; //值矩陣 float OLDV[MAX_N][MAX_N]; //保存舊的val值 char S[MAX_N][MAX_N]; //狀態矩陣,直觀存儲運動方向 char OLDS[MAX_N][MAX_N]; //舊的狀態矩陣 char A[4] = { 'n','e','s','w' }; //Action ,'↑','→','↓','←' int dx[] = { -1,0,1,0 }, dy[]= { 0,1,0,-1 }; //四向移動 int r = -1; //獎勵值 float disc = 0.57f; //折扣值 float en = 0.00000001f; //sub小於這個數時收斂 // 獲取輸出流的句柄 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //初始化 void init() { for (int i = 0; i < MAX_N; i++) for (int j = 0; j < MAX_N; j++) { S[i][j] = 'n'; //初始化為向右走 OLDS[i][j] = 'n'; } OLDS[0][0] = S[0][0] = '#'; OLDS[3][3] = S[3][3] = 'E'; //目標點 OLDS[2][2] = S[2][2] = 'E'; //障礙 OLDS[2][1] = S[2][1] = '#'; OLDS[2][3] = S[2][3] = '#'; } int getIndex(char ch) { switch (ch) { case 'n': return 0; case 'e': return 1; case 's': return 2; case 'w': return 3; default: return -1; } } void printVal() { puts("----------------value-----------------"); for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { printf("%f ", V[i][j]); } puts("\n"); } } void printState() { puts("----------------State-----------------"); //打印策略 for (int i = 0; i < MAX_N; i++) { printf(" "); for (int j = 0; j < MAX_N; j++) { switch (S[i][j]) { case 'n': printf("↑ "); break; case 'e': printf("→ "); break; case 's': printf("↓ "); break; case 'w': printf("← "); break; default: printf("%c ", S[i][j]); break; } } puts("\n"); } } void PolicyEvaluation(){ float sub; int cnt = 0; do{ sub = 0.0f; for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { if (S[i][j] != '#' && S[i][j] != 'E') { //不是障礙物及終點 float val = V[i][j]; int k = getIndex(S[i][j]); //根據狀態得出移動下標 int x = i + dx[k], y = j + dy[k]; if (x >= 0 && x < MAX_N && y >= 0 && y < MAX_N && S[x][y] != '#') { V[i][j] = r + disc * OLDV[x][y]; } sub = max(sub, fabs(val - V[i][j])); } } } //把新的val值拷貝到舊的數組中 for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { OLDV[i][j] = V[i][j]; } } printf("cnt : %d\n", ++cnt); printVal(); } while (sub > en); } void PolicyImprovement() { while (true) { bool stable = true; for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { if (OLDS[i][j] != '#' && OLDS[i][j] != 'E') { char oldact = OLDS[i][j]; int k = 4; float ma = INF; while (k--) { int x = i + dx[k], y = j + dy[k]; if (x >= 0 && x < MAX_N && y >= 0 && y < MAX_N && OLDS[x][y] != '#') { float round = r + disc * OLDV[x][y]; if (round - ma > en) { ma = round; //改變Val值 S[i][j] = A[k]; //改變當前狀態的最佳策略 char ch = A[k]; } } } if (oldact != S[i][j]) stable = false; } } } //將所有新狀態拷貝至舊狀態 for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { OLDS[i][j] = S[i][j]; } } if (stable) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED); printf("\n--------------------------STOP-----------------------\n"); printVal(); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xA); //亮綠 printState(); break; } else { PolicyEvaluation(); //繼續策略迭代 } } } int main() { init(); PolicyEvaluation(); PolicyImprovement(); return 0; }
2)值迭代
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <Windows.h> using namespace std; const int MAX_N = 4; const float INF = -999999999.0f; float V[MAX_N][MAX_N]; //值矩陣 float OLDV[MAX_N][MAX_N]; //保存舊的val值 char S[MAX_N][MAX_N]; //狀態矩陣,直觀存儲運動方向 //float P[MAX_N*MAX_N][MAX_N*MAX_N]; //狀態轉移概率矩陣 char A[4] = { 'n','e','s','w' }; //Action ,'↑','→','↓','←' int dx[] = { -1,0,1,0 }, dy[] = { 0,1,0,-1 }; //四向移動 int r = -1; //獎勵值 float disc = 1.0f; //折扣值 float en = 0.0000001f; //sub小於這個數時收斂 //初始化 void init() { for (int i = 0; i < MAX_N; i++) for (int j = 0; j < MAX_N; j++) { S[i][j] = 'w'; //初始化為向右走 } S[0][2] = '#'; //#障礙 S[3][1] = 'E'; //E目標點 S[2][2] = '#'; S[1][2] = 'E'; S[1][2] = '#'; } int getIndex(char ch) { switch (ch) { case 'n': return 0; case 'e': return 1; case 's': return 2; case 'w': return 3; default: return -1; } } void printVal() { puts("----------------value-----------------"); for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { printf("%.4f ", V[i][j]); } puts("\n"); } } void ValueIteration() { float sub = 0; int cnt = 0; do { sub = 0; for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { if (S[i][j] != '#' && S[i][j] != 'E') { //不是障礙物及終點 float val = V[i][j]; int k = 4; int ma = INF; while (k--) { int x = i + dx[k], y = j + dy[k]; if (x >= 0 && x < MAX_N && y >= 0 && y < MAX_N && S[x][y] != '#') { float round = r + disc * OLDV[x][y]; if (ma < round) { ma = round; //改變Val值 } } } if (ma != INF) V[i][j] = ma; //V[i][j]取得周圍環境的最大值 sub = max(sub, fabs(val - V[i][j])); } } } //把新的val值拷貝到舊的數組中 for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { OLDV[i][j] = V[i][j]; } } printf("cnt : %d\n", ++cnt); printVal(); } while (sub >= en); //更新最佳策略 for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { if (S[i][j] != '#' && S[i][j] != 'E') { //不是障礙物及終點 int k = 4; int ma = INF; while (k--) { int x = i + dx[k], y = j + dy[k]; if (x >= 0 && x < MAX_N && y >= 0 && y < MAX_N && S[x][y] != '#') { float round = r + disc * OLDV[x][y]; if (ma < round) { ma = round; //改變Val值 S[i][j] = A[k]; } } } } } } SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xA); //亮綠 //打印策略 for (int i = 0; i < MAX_N; i++) { for (int j = 0; j < MAX_N; j++) { char ch; switch (S[i][j]) { case 'n': printf("↑ "); break; case 'e': printf("→ "); break; case 's': printf("↓ "); break; case 'w': printf("← "); break; default: printf("%c ", S[i][j]); break; } } puts("\n"); } } int main() { init(); ValueIteration(); return 0; }