質問するログイン新規登録

Q&A

解決済

2回答

1752閲覧

乱数を発生させるときのseed指定の際にcurrentTimeMillisを使った場合と指定しない場合の違い

退会済みユーザー

退会済みユーザー

総合スコア0

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

1クリップ

投稿2018/11/20 01:29

1

1

前提・実現したいこと

複数人のじゃんけんプログラムです。
人数、回数の順に入力していただくと、じゃんけんの結果が表示されます。
※Judgemanクラスのjudgeメソッドの冒頭でentrySetを使って0, 1, 2が
何回出ているかを表示させてみましたが, currentTimeMillisを使うと
極端に一つの数字が多く発生する場合があることがわかりました。

発生している問題・エラーメッセージ

エラーは出ません。 10人, 100人で100回じゃんけんをしたときの結果が 乱数を発生させるときに Random r = new Random(System.currentTimeMillis()); を使うと引き分けが3割程度発生しますが Random r = new Random(); を使うと引き分けが9割以上発生します。 本来ならば後者の結果になるらしいのですが, なぜ違いが発生するのかを教えていただきたいです。 というのも, 単純に乱数を発生させた場合(下のSmp.java)では前者後者どちらの 実行結果も変わりはないように思えるからです.

該当のソースコード

java

1/////Smp.javaです。これのみじゃんけんプログラムではありません。 2import java.util.*; 3 4public class Smp{ 5 public static void main(String[] args){ 6 int cnt0 = 0; 7 int cnt1 = 0; 8 int cnt2 = 0; 9 //前者です 10 //long seed = System.currentTimeMillis(); 11 //java.util.Random r = new Random(seed); 12 //後者です 13 //java.util.Random r = new Random(); 14 15 for(int i = 0; i < 100; i++){ 16 int result = r.nextInt(3); 17 if(result == 0) cnt0++; 18 else if(result == 1) cnt1++; 19 else cnt2++; 20 } 21 22 System.out.println(cnt0); 23 System.out.println(cnt1); 24 System.out.println(cnt2); 25 26 27 } 28} 29 30/////ここから下の4つのクラスがじゃんけんプログラムです///// 31/////Main.javaです 32import java.util.*; 33 34public class Main{ 35 public static void main(String[] args){ 36 Scanner sc = new Scanner(System.in); 37 int n = sc.nextInt(); 38 int m = sc.nextInt(); 39 Player[] p = new Player[n]; 40 for(int i = 0; i < n; i++){ 41 p[i] = new Player(i); 42 } 43 Judgeman man = new Judgeman(); 44 Counter cnt = new Counter(n, m); 45 46 for(int i = 0; i < m; i++){ 47 man.startGame(p, cnt); 48 int result = man.judge(); 49 cnt.recordResultGame(result); 50 man.clearMap(); 51 } 52 53 cnt.showResult(p); 54 55 } 56} 57 58/////Player.javaです 59import java.util.*; 60 61public class Player{ 62 public final int ROCK = 0; 63 public final int PAPER = 1; 64 public final int SCISSORS = 2; 65 private static int tie_time = 0; 66 private int id; 67 private int rock_win = 0; 68 private int paper_win = 0; 69 private int scissors_win = 0; 70 71 //前者です 72 //long seed = System.currentTimeMillis(); 73 //java.util.Random r = new Random(seed); 74 //後者です 75 //java.util.Random r = new Random(); 76 77 public Player(int id){ 78 this.id = id; 79 } 80 81 public int getID(){ 82 return this.id; 83 } 84 85 public int showHand(){ 86 int hand = r.nextInt(3); 87 return hand; 88 } 89 90 public static void updateTie(){ 91 Player.tie_time++; 92 } 93 94 public void updateRock(){ 95 this.rock_win++; 96 } 97 98 public void updatePaper(){ 99 this.paper_win++; 100 } 101 102 public void updateScissors(){ 103 this.scissors_win++; 104 } 105 106 public static int getTie_time(){ 107 return Player.tie_time; 108 } 109 110 public int getRockWin(){ 111 return this.rock_win; 112 } 113 114 public int getPaperWin(){ 115 return this.paper_win; 116 } 117 118 public int getScissorsWin(){ 119 return this.scissors_win; 120 } 121 122} 123 124/////Judegman.javaです 125import java.util.*; 126 127public class Judgeman{ 128 public final int ROCK = 0; 129 public final int PAPER = 1; 130 public final int SCISSORS = 2; 131 public final int TIE = -1; 132 133 Map<Integer, Integer> map = new HashMap<>(); 134 135 public void startGame(Player[] p, Counter cnt){ 136 for(Player player : p){ 137 int result = player.showHand(); 138 map.put(result, map.getOrDefault(result, 0) + 1); 139 cnt.recordHand(result, player.getID()); 140 } 141 } 142 143 public int judge(){ 144 System.out.println(this.map.entrySet()); 145 if(this.map.size() == 1 || this.map.size() == 3){ 146 return TIE; 147 } 148 if(!this.map.containsKey(ROCK)){ 149 return SCISSORS; 150 } 151 return map.containsKey(PAPER) ? PAPER : ROCK; 152 } 153 154 public void clearMap(){ 155 this.map.clear(); 156 } 157 158} 159 160/////Counter.javaです 161import java.util.*; 162 163public class Counter{ 164 public final int ROCK = 0; 165 public final int PAPER = 1; 166 public final int SCISSORS = 2; 167 public final int TIE = -1; 168 private int n; 169 private int m; 170 private int time = 0; 171 private int[][] array; 172 173 public Counter(int n, int m){ 174 this.n = n; 175 this.m = m; 176 makeArray(this.n + 1, this.m); 177 } 178 179 public void makeArray(int x, int y){ 180 this.array = new int[x][y]; 181 } 182 183 public void recordHand(int hand, int id){ 184 array[id][this.time] = hand; 185 } 186 187 public void recordResultGame(int result){ 188 if(result == TIE){ 189 Player.updateTie(); 190 } 191 array[this.n][this.time] = result; 192 this.time++; 193 } 194 195 public void showResult(Player[] p){ 196 System.out.println("結果発表"); 197 for(Player player : p){ 198 199 for(int i = 0; i < this.m; i++){ 200 if(array[this.n][i] == array[player.getID()][i]){ 201 switch(array[player.getID()][i]){ 202 case ROCK: 203 player.updateRock(); 204 break; 205 case PAPER: 206 player.updatePaper(); 207 break; 208 case SCISSORS: 209 player.updateScissors(); 210 break; 211 } 212 } 213 } 214 215 System.out.println("P" + (player.getID()+1) + "\t" + (player.getRockWin() + player.getPaperWin() + 216 player.getScissorsWin()) + "勝" + " " + "グー" + player.getRockWin() + "勝" + ", " + 217 "パー" + player.getPaperWin() + "勝" + ", " + "チョキ" + player.getScissorsWin() + "勝"); 218 } 219 System.out.println("引分\t" + Player.getTie_time() + "回です"); 220 } 221 222} 223 224 225

補足情報(FW/ツールのバージョンなど)

java 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)

LouiS0616が👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

guest

この質問の投稿者はすでに退会しているため、質問者からの返信がない点にご留意ください。

回答2件

0

ベストアンサー

java.util.Randomのソースコードによると

/** * Creates a new random number generator. This constructor sets * the seed of the random number generator to a value very likely * to be distinct from any other invocation of this constructor. */ public Random() { this(seedUniquifier() ^ System.nanoTime()); }

↑のように引数無しコンストラクタ内部ではSystem.nanoTime()に依存する値を引数として引数有りコンストラクタを呼び出しています。
currentTimeMillisは1ミリ秒(1ナノ秒の1000倍)の間同じ値なので、取得した乱数が同じになりやすいのです。

投稿2018/11/20 01:50

tkturbo

総合スコア5572

tkturbo

2018/11/20 01:52

1ミリ秒(1ナノ秒の1000倍)←1ナノ秒の1000000倍が正解
退会済みユーザー

退会済みユーザー

2018/11/20 05:15

回答ありがとうございます. 貼っていただいたリンク先のソースコードやAPI等見ましたが, しばらく理解できそうにありません. とりあえず, currentTimeMillisはある時点を参照する時間が長くいので, 異なる乱数が欲しい時は nanoTimeを使えるRandomメソッドを使って乱数を発生させたほうがよい, という理解で妥協しておきます.
guest

0

前提

seedに同じ値を与えると、常に同じ乱数列が生成されます。

Java

1import java.util.*; 2 3class Main { 4 public static void main(String[] args) { 5 int seed = 42; 6 7 Random r1 = new Random(seed); 8 Random r2 = new Random(seed); 9 10 for(int i = 0; i < 10; ++i) { 11 System.out.printf("%d %d\n", r1.nextInt(), r2.nextInt()); 12 } 13 } 14}

実行結果 Wandbox

2 2 0 0 0 0 2 2 0 0 1 1 2 2 2 2 1 1 2 2

これは、乱数列がseedに対して決定的に選ばれているためです。

本題

Java

public class Player{
...
long seed = System.currentTimeMillis();
java.util.Random r = new Random(seed);

seedの値は、Playerインスタンスが生成されたときに決定されます。

ご提示のコードではPlayerインスタンスを一気に生成していますね。
短時間に複数のインスタンスが作られたとしたら、seed値が被ってしまう恐れがあります。

対策

ミリ秒を使わないことです。
これについては、既にあるtkturboさんの回答に詳しいです。

投稿2018/11/20 05:40

LouiS0616

総合スコア35682

退会済みユーザー

退会済みユーザー

2018/11/20 23:12

回答ありがとうございます. 「前提」欄の"seedに対して乱数列が決定的に選ばれる"というのは初めて知りました. 実際に, Main.javaのPlayerクラスのインスタンスを生成している箇所で for(int i = 0; i < n; i++){ p[i] = new Player(i); System.out.println(p[i].seed); } として, 10人のじゃんけんとして実行してみると 1542754963526 1542754963527 1542754963527 1542754963528 1542754963528 1542754963528 1542754963528 1542754963528 1542754963528 1542754963528 という風にかなりの人数が同じseed値を持っていることがわかりました. これからはミリ秒を使わずナノ秒のRandom( )を使いたいと思います.
guest

この質問の投稿者はすでに退会しているため、質問者からの返信やベストアンサーの選定がない点にご留意ください。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.25%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問