前提・実現したいこと
NNのグリッドワールドの任意の位置にゴールを配置する.(これは一回だけです)
NNのグリッドワールドの任意の位置にエージェントを配置する.(これは繰り返し回数loop回です)
1番始めは, 任意のマスの上下左右は等確率で移動するようにするが,
移動の上限回数以内にゴールした場合は,ゴールから一定のステップだけ遡って
報酬を与える.(このときゴールから遠ざかるほど報酬は小さくなる.)
つまり,あるマスのある方向が,ゴールしたときに通られれば通られるほどその方向は確率が
大きくなる.(注意)繰り返し回数が終わるまで確率は更新しつづけるので上下左右を等確率で
設定するのは実行時の最初の1回だけです.)
(また繰り返し回数分エージェントは作り, それぞれのスタート位置も異なります.)
これを繰り返し回数だけやったのちに,(後半戦突入)再び繰り返し回数だけ任意の位置にエージェントを配置して
ゴールを目指すが,このときの移動は確率に基づくのでは無く,前半で設定した確率のうち一番
大きいものを移動方向として採用する.また,ゴールの位置は1行目で配置した位置と同じ位置である.
このときゴールした回数を求める.
といったことをしたいです.
拙い日本語で申し訳ありません.
発生している問題・エラーメッセージ
エラーメッセージは出ておりません. 同じnについて、ゴールの回数が実行ごとに大幅に変わってしまうことに違和感があって, 僕のコードがおかしいと思うので指摘していただきたいです. 乱数を使っているので実行結果が変わること自体は感覚的に納得しますが たとえば, n=5のときに実行結果が下は0から上は66まで出るのはさすがに変だと思います. また, 報酬を与える際のやり方もうまいやり方がわかりません.(GridクラスのsetArrowメソッド) (確率なので合計だけでなく各方向も0以上1以下に収めるべきかなと思うのですが、 例えば0.01, 0.01, 0.01, 0.97から報酬を0.97をもつ方向に0.5あげると0.97が 1を超えて0.01は0を下回りますが, 0.97を1で押さえつけてしまうと正しい評価にならないんじゃ ないかとも思います.)
該当のソースコード
java
1/////Main.javaです///// 2import java.util.*; 3 4public class Main{ 5 public static void main(String[] args){ 6//原則入力を受け付けますが,入力の手間を省くためここでは予め値を代入しています 7 8 /*Scanner sc = new Scanner(System.in); 9 System.out.print("マップの大きさ:"); 10 int n = sc.nextInt(); 11 System.out.print("ステップ数:"); 12 int T = sc.nextInt(); 13 System.out.print("繰り返し回数:"); 14 int loop = sc.nextInt(); 15 System.out.print("報酬:"); 16 double P = sc.nextDouble(); 17 System.out.print("エージェントの寿命:"); 18 int life = sc.nextInt(); 19 int tactics = 0; 20 int cnt = 0;*/ 21//各変数の値は適当です. 22 int n = 5;//グリッドワールドの大きさ, ここでは25(5*5)マスあることになります. 23 int T = 50;//ゴールして報酬を与える際, ゴールから遡るステップ数です. 24 int loop = 100;//繰り返し回数です. 25 double P = 0.5;//ゴールした際に与える報酬です. 26 int life = 200;//1つのエージェントがゴールを目指すときに動ける上限回数です. 27 int tactics = 0;//ゴールした際に報酬を与えるかカウントするかの違いのために使います. 28 //(続き)tactics == 2 のときは<前提・実現したいこと>の(後半戦)に当たります. 29 int cnt = 0;//後半戦におけるゴールへの到達回数です. 30 31 Agent.setVariable(n, T, life, P); 32 Agent.setGoal(); 33 Grid grid = new Grid(n); 34 35 for(tactics = 1; tactics <= 2; tactics++){ 36 for(int i = 0; i < loop; i++){ 37 boolean ans = true; 38 Agent agent = new Agent(); 39 40 for(int j = 0; j < life; j++){ 41 //ans==1のときはゴールしたときです. 42 ans = agent.askDirectory(grid, j, tactics); 43 if(ans){ 44 if(tactics == 1){ 45 //報酬を与えます. 46 agent.giveReward(grid, j); 47 } 48 //到達回数を数えます. 49 if(tactics == 2) cnt++; 50 break; 51 } 52 } 53 } 54 } 55 System.out.println(cnt); 56 } 57} 58 59/////Agent.javaです///// 60import java.util.*; 61 62public class Agent{ 63 private final int UP = 0; 64 private final int RIGHT = 1; 65 private final int DOWN = 2; 66 private final int LEFT = 3; 67 68 private int[] history;//移動方向を保存する配列 69 private int[][] start = new int[1][2]; 70 private int[][] tmp = new int[1][2]; 71 private static int[][] goal = new int[1][2]; 72 private static int n; 73 private static int T; 74 private static int life; 75 private static double P; 76 77//エージェントごとに(繰り返し回数の1回1回で)スタート位置は異なります. 78 public Agent(){ 79 Random r = new Random(); 80 history = new int[Agent.life];//移動の上限回数分用意します. 81 this.start[0][0] = this.tmp[0][0] = r.nextInt(Agent.n); 82 this.start[0][1] = this.tmp[0][1] = r.nextInt(Agent.n); 83 //System.out.println("start_x:" + this.start[0][0]); 84 //System.out.println("start_y:" + this.start[0][1]); 85 } 86 87 public static void setVariable(int n, int T, int life, double P){ 88 Agent.n = n; 89 Agent.T = T; 90 Agent.life = life; 91 Agent.P = P; 92 } 93 94//ゴールの位置は, 1回の実行においてすべてのエージェントに共通なのでクラス変数にします. 95 public static void setGoal(){ 96 Random r = new Random(); 97 Agent.goal[0][0] = r.nextInt(Agent.n); 98 Agent.goal[0][1] = r.nextInt(Agent.n); 99 } 100 101 public boolean askDirectory(Grid g, int life, int tactics){ 102 boolean flag = false; 103 104//移動先がグリッドワールド以内であるとおきwhile文を抜けます. 105 while(flag == false){ 106 int next_line = this.tmp[0][0]; 107 int next_row = this.tmp[0][1]; 108 int d = 0; 109//移動方向を決めるときに, 確率的なのがtactics==1, 確率が一番大きい方向を使うのがelse(tactics==2)です. 110 if(tactics == 1){ 111 d = g.showDirectoryLearn(this.tmp[0][0], this.tmp[0][1]); 112 }else{ 113 d = g.showDirectoryAssess(this.tmp[0][0], this.tmp[0][1]); 114 } 115 116 switch(d){ 117 case UP: 118 next_line--; 119 break; 120 case RIGHT: 121 next_row++; 122 break; 123 case DOWN: 124 next_line++; 125 break; 126 case LEFT: 127 next_row--; 128 break; 129 } 130 131 if(0<=next_line && next_line<Agent.n && 0<=next_row && next_row<Agent.n ){ 132 this.tmp[0][0] = next_line; 133 this.tmp[0][1] = next_row; 134 history[life] = d; 135 flag = true; 136 } 137 } 138 139 if(this.tmp[0][0] == Agent.goal[0][0] && this.tmp[0][1] == Agent.goal[0][1]){ 140 return true; 141 }else{ 142 return false; 143 } 144 145 } 146 147 public void giveReward(Grid grid, int j){ 148 int d = 0; 149 double reward = 0; 150 int next_line = Agent.goal[0][0]; 151 int next_row = Agent.goal[0][1]; 152 153 int target = 0; 154 int idx = j; 155 int t = 1; 156 157//例えば1ステップ目でゴールしたとき, j=0. ということは1ステップ目の方向はhistory[0]に入っている. 158//tは遡った回数です. Tステップだけ遡り, かつ添字は0以上のときだけ報酬を考える. 159 while(idx >= 0 && t <= Agent.T){ 160 d = history[idx]; 161 162 switch(d){ 163 case UP: 164 next_line++; 165 break; 166 case RIGHT: 167 next_row--; 168 break; 169 case DOWN: 170 next_line--; 171 break; 172 case LEFT: 173 next_row++; 174 break; 175 } 176 177 if(next_line == 0){ 178 target = next_row; 179 }else{ 180 target = next_line * Agent.n + next_row; 181 } 182//報酬の式は[ゴールからtステップ遡るグリッドにおいて行動d(dは上下左右のどれかを表します) 183//を選択したとき, d^tとして, 確率をlength_old(d^t)を次の式によりlength_new(d^t)に変更します. 184//length_new(d^t) = length_old(d^t) + P * (T - t + 1)/T (t = 1, 2, ・・・, T]です. 185//ここでは, +の後ろを計算してrewardに入れて, それをsetArrowを使ってグリッドに設定しに行きます. 186 reward = this.P * (Agent.T - t + 1) / Agent.T; 187 188 grid.setArrow(target, d, reward); 189 190 idx--; 191 t++; 192 } 193 } 194 195} 196 197/////Grid.javaです///// 198import java.util.*; 199 200public class Grid{ 201 private final int UP = 0; 202 private final int RIGHT = 1; 203 private final int DOWN = 2; 204 private final int LEFT = 3; 205 206 private int[][] map; 207 private double[][] arrow; 208 private int n; 209 210 private List<Integer> arylist = new ArrayList<>(); 211 212 213 public Grid(int n){ 214 this.n = n; 215 this.map = new int[n][n];//いらないっぽいです. 216 this.arrow = new double[n*n][4];//すべてのマスの上下左右4方向の確率を入れるための配列 217 setArrowEqual(n); 218 } 219 220//実行時一番はじめに4方向の確率を等しくします. 221 public void setArrowEqual(int n){ 222 for(int i = 0; i < n*n; i++){ 223 for(int j = 0; j < 4; j++){ 224 this.arrow[i][j] = 0.25; 225 } 226 } 227 } 228 229 public int showDirectoryLearn(int line, int row){ 230 int target = 0; 231//エージェントのx座標(line), y座標(row)に対応するarrowの添字を見つけます. 232//1行n列(map[0][n-1])に対応するのはarrow[n-1], 2行n列(map[1][2n-1])に対応するのはarrow[2n-1] 233//なので, これを一般化したものです. 234 if(line == 0){ 235 target = row; 236 }else{ 237 target = line * this.n + row; 238 } 239 240//最初は等確率なので, やってることは0.25以下は上, 0.50以下は右という風な設定です. 241 double up = this.arrow[target][0]; 242 double right = up + this.arrow[target][1]; 243 double down = right + this.arrow[target][2]; 244 double left = down + this.arrow[target][3]; 245 246 Random r = new Random(); 247 double value = r.nextDouble(); 248 if(value <= up){return UP;} 249 if(value <= right){return RIGHT;} 250 if(value <= down){return DOWN;} 251 return LEFT; 252 } 253 254//tactics==2のときは確率が一番大きいものを選びます. 255//ただし複数の方向が最大値をもつときはそれらの中からランダムに方向を選びます. 256 public int showDirectoryAssess(int line, int row){ 257 int target = 0; 258 int cnt = 0; 259 double max = Double.MIN_VALUE; 260 int ans = 0; 261 262 if(line == 0){ 263 target = row; 264 }else{ 265 target = line * this.n + row; 266 } 267//最大値を求めます. 268 for(int i = 0; i < 4; i++){ 269 max = Math.max(this.arrow[target][i], max); 270 } 271//最大値をもつ個数を知ります. 272 for(int i = 0; i < 4; i++){ 273 if(max == this.arrow[target][i]) this.arylist.add(i); 274 } 275 276 if(this.arylist.size() == 1){ 277 ans = this.arylist.get(0); 278 }else{ 279 Random r = new Random(); 280 int value = r.nextInt(this.arylist.size()); 281 ans = this.arylist.get(value); 282 } 283 284 this.arylist.clear(); 285 return ans; 286 } 287//このやり方では, 合計確率は1ですが, ある方向が1を超えたり0を下回る可能性があり, 288//うまいやり方が見つかりません. 289 public void setArrow(int target, int d, double reward){ 290 for(int i = 0; i < 4; i++){ 291 this.arrow[target][i] -= reward / 3; 292 } 293 this.arrow[target][d] += 4 * reward / 3; 294 } 295} 296
補足情報(FW/ツールのバージョンなど)
java version "10.0.2" 2018-07-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.2+13)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)
回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2018/12/06 05:28
2018/12/09 02:37