1

私は、サイコロ ゲーム (通常の 6 面ダイス) をプレイする短いプログラムを作成しようとしています。最初のロールの番号がスコアに追加されます。最初のロールの後、6 をロールすると、ゲームが停止し、スコアが記録されます (6 は追加されません)。最初のロールで 6 がロールされた場合、それは問題なく、他の 1 から 5 までの数字と同じように追加されます。私はこのゲームを何度も実行して、バスト前のスコアの長いリストを作成しようとしています (aバストが丸められている 6)。これらのスコアを最小から最大の順に並べ替え、停止するのに最適なスコアであるリストの中央値を見つけます。

なんらかの理由で、プログラムを実行すると 13 を取得し続けますが、答えが 15 であることはわかっています。Java で Random を使用すると、中央値に何らかの影響がありますか? Random がどのように数字を生成するのか、またそれらが均等な機会で生成されるのかどうかは正確にはわかりません。また、飛び出してうまくいかないものはありますか?

import java.util.*;
public class DiceRoller {

private static Random r = new Random();
private static final int games = 10001;
private static int[] totalScores = new int[games];
private static int index = 0;

public static void main(String[] args) {
    int score = 0; boolean firstRoll = true;
    while (index < games) {
        int roll = roll();
        if (firstRoll) {
            score += roll;
            firstRoll = false; 
        } else {
            if (roll == 6) {
                totalScores[index] = score;
                index++; 
                score = 0; firstRoll = true;
            } else {
                score += roll;
            }
        }
    } 
    System.out.println("The median is " + median() + ".");
}

public static int roll() {
    return r.nextInt(6) + 1;
}

public static int median() {
    Arrays.sort(totalScores);
    int temp = totalScores[games / 2];
    return temp;
}

}
4

3 に答える 3

2

それが13正しい結果だからです。ちょっとした数学: がこれらのゲームのいずれかのスコアを表す確率変数である場合、 の確率生成関数S考えることができます。ゲームの説明から、この確率生成関数は次の式を満たします。 f(z)S

f(z) = (z + z^2 + z^3 + z^4 + z^5 + z^6) / 36 + f(z)(z + z^2 + z^3 + z^4 + z^5) / 6

これには、少し考えたり、この種の構造に慣れたりする必要があります。右側の左側の項は、単純な 2 ロール ゲームで 1 から 6 になる確率を考慮に入れています。f(z) を含む右辺の項は、3 つ以上のロールを含むゲームを考慮し、最終的なプレ 6 ロール (1 から 5 の範囲内にある必要があります) とその前のロールで表現します。fagainを使用して再帰的に表現できます。

とにかく、ここまでたどり着いたら、fの有理関数として記述できるように並べ替えてz、べき級数として展開できます。これは次のように始まります。

f(z) = 1/36*z + 7/216*z^2 + 49/1296*z^3 + 343/7776*z^4 + 2401/46656*z^5 + 16807/279936*z^6 + 63217/1679616*z^7 + 388087/10077696*z^8 + 2335585/60466176*z^9 + 13681927/362797056*z^10 + 77103313/2176782336*z^11 + 409031959/13060694016*z^12 + 2371648321/78364164096*z^13 + 13583773735/470184984576*z^14 + ...

(私はこれを取得するためにPari/GPを使用しました。)

の係数はz^k、ゲームの値が である確率を表しkます。したがって、スコアが 1 になる確率は 36 分の 1 で、スコアが 2 になる確率は 216 分の 7 です。最初の 12 個の係数0.472828864487196328...の合計は であり、最初の 13 個の係数の合計は です0.5030933144224321950968...。したがって、中央値は確かに 13 です。

独立したチェックを提供するために、簡単な Python プログラムを作成しました。

from __future__ import division
import random

def roll():
    return random.randint(1, 6)

def play():
    score = roll()
    while True:
        throw = roll()
        if throw == 6:
            break
        score += throw
    return score

all_scores = sorted(play() for _ in xrange(1000001))
print "median is: ",all_scores[len(all_scores) // 2]
print "fraction of scores <= 12: ",all_scores.index(13) / len(all_scores)
print "fraction of scores <= 13: ",all_scores.index(14) / len(all_scores)

案の定、結果は次のとおりです。

iwasawa:~ mdickinson$ python dice_game.py 
median is:  13
fraction of scores <= 12:  0.472811527188
fraction of scores <= 13:  0.502863497137

あなたの質問に答えるために、あなたが見ている結果は、Java の乱数生成に何らかの弱点があるという証拠ではありません。

于 2011-09-04T19:27:39.543 に答える
1

Random は完全にランダムではなく、いくつかの欠点があります。ただし、この使用例では、違いに気付く可能性はほとんどありません。1 から 6 までのすべての値の可能性が等しいと仮定できます。

比較のために、すべての値を記録するのではなく、合計の発生回数をカウントする別のソリューションを次に示します。ご覧のとおり、これは 1000 倍のゲームを持っていてもうまく機能します。これは、結果の数が少なく、重複の数が多い場合に最適です。自然にソートされます。

import java.util.Random;

public class DiceRoller {
  private static final int MAX_VALUE = 300; // assume at most this total
  private static final int GAMES = 10000001;

  public static void main(String... args) {
    int[] count = new int[MAX_VALUE];
    Random rand = new Random();
    for (int i = 0; i < GAMES; i++)
      count[totalScore(rand)]++;
    System.out.println("The median is " + median(count, GAMES) + ".");
  }

  private static int median(int[] count, int games) {
    int findTotal = games/2;
    for (int i = 0; i < count.length; i++) {
      findTotal -= count[i];
      if (findTotal <= 0) return i;
    }
    throw new AssertionError();
  }

  private static int totalScore(Random rand) {
    int total = rand.nextInt(6) + 1;
    for(int n;(n = rand.nextInt(6) + 1) != 6;)
      total += n;
    return total;
  }
}
于 2011-09-04T07:04:58.947 に答える
0

結果の分布を示すコードを次に示します。質問の答えにはなりませんが、研究に役立つかもしれません。

package so7297660;

import java.util.Random;

public class DiceRoller {

  private static final int N = 10000000;
  private static final Random r = new Random();
  private static final int[] result = new int[100];

  public static int roll() {
    return r.nextInt(6) + 1;
  }

  private static int singleGame() {
    int score = roll();
    while (true) {
      int roll = roll();
      if (roll == 6) {
        return score;
      } else {
        score += roll;
      }
    }
  }

  private static int median() {
    int n = 0;
    for (int i = 0; i < result.length; i++) {
      if (n + result[i] >= N / 2) {
        return i;
      }
      n += result[i];
    }
    throw new IllegalStateException();
  }

  public static void main(String[] args) {
    for (int i = 0; i < N; i++) {
      int score = singleGame();
      int index = Math.min(score, result.length - 1);
      result[index]++;
    }
    for (int i = 0; i < result.length; i++) {
      System.out.println(i + "\t" + result[i]);
    }
    System.out.println("median\t" + median());
  }

}
于 2011-09-04T07:11:53.517 に答える