0 から 91 までの数値を変換してロールに変換する方法は既に知っています (前の質問への回答から)。N >> 91 の場合、N 要素の配列を作成することをお勧めします。最初の 91 要素を 0...90 で埋め、カウンターA
を 91 に設定します。次に、0 と A の間の数値を選択し、対応する要素を選択します。配列から、乗算の問題に変換します。答えが間違っている場合は、問題の番号を配列の末尾に追加し、1 ずつ増やしますA
。
これにより、サンプリングの頻度が問題が間違って解決された回数を表す配列が作成されます。ただし、次に質問されたときに問題が正しく解決された場合、頻度が再び低下することはありません。
代替のより良い解決策であり、あなたのものに少し近い (ただし異なる) 解決策は、91 個の周波数の配列を作成し、それぞれが最初は 1 に設定され、合計 (最初は 91) を追跡します。しかし今、乱数 (0 と合計の間) を選択すると、累積合計が乱数よりも大きくなるまで配列をトラバースします。ビンの数は選択したロールであり、以前に導出された式でそれを変換します。 . 答えが間違っている場合は、ビンをインクリメントして合計を更新します。それが正しければ、合計を減分しますが、1 未満の値になることはなく、合計を更新します。繰り返す。
これはまさにあなたが求めているものを与えるはずです: 91 個の数値 (「ビン」) の配列が与えられた場合、そのビンの確率がその値に比例するようにビンをランダムに選択します。ビンのインデックスを返します (これは、以前の方法を使用して数値の組み合わせに変換できます)。この関数は、ビン (頻度) 配列を最初のパラメーターとして、累積合計を 2 番目のパラメーターとして呼び出されます。最初の n 個の要素の累積合計が、度数の合計でスケーリングされた乱数を最初に超える場所を調べます。
private int chooseBin(float[] freq, float fsum) {
// given an array of frequencies (probabilities) freq
// and the sum of this array, fsum
// choose a random number between 0 and 90
// such that if this function is called many times
// the frequency with which each value is observed converges
// on the frequencies in freq
float x, cs=0; // x stores random value, cs is cumulative sum
int ii=-1; // variable that increments until random value is found
x = Math.rand();
while(cs < x*fsum && ii<90) {
// increment cumulative sum until it's bigger than fraction x of sum
ii++;
cs += freq[ii];
}
return ii;
}
フィードした確率分布 (赤い線) とまったく同じように見えるヒストグラム (青いバー) が得られることを確認しました。
(注 - これは matlab でプロットされたものであるため、X は 0 から 90 ではなく 1 から 91 になります)。
これは別のアイデアです(これは実際には質問に答えているわけではありませんが、潜在的にさらに興味深いものです):
一様分布以外のものをサンプリングすることで、特定の問題を選択する確率を歪めることができます。たとえば、一様にサンプリングされた確率変量の 2 乗では、小さい数値が優先されます。これにより、興味深い可能性が得られます。
まず、91 の数字をランダムにシャッフルします。
次に、不均一な分布 (より小さい数を優先する分布) から数を選びます。数字はランダムにシャッフルされているため、実際に選択される可能性は等しくなります。問題 (選択された番号で表される) が正しく解決された場合、問題番号を「スタックの一番上」に移動します。ここで、再度選択される可能性は最も低くなります。プレーヤーがそれを間違えた場合、スタックの一番下に移動され、再び選択される可能性が最も高くなります。時間の経過とともに、困難な問題はスタックの一番下に移動します。
のバリエーションを使用して、さまざまな程度の歪曲でランダムな分布を作成できます。
roll = (int)(91*(asin(Math.rand()*a)/asin(a)))
1にa
近づけるにつれて、関数は小さい数値を優先する傾向があり、大きい数値の確率はほぼゼロです。
次のコード セクションは、私が説明したことを実行すると思います。
private int[] chooseProblem(float bias, int[] currentShuffle) {
// if bias == 0, we choose from uniform distribution
// for 0 < bias <= 1, we choose from increasingly biased distribution
// for bias > 1, we choose from uniform distribution
// array currentShuffle contains the numbers 0..90, initially in shuffled order
// when a problem is solved correctly it is moved to the top of the pile
// when it is wrong, it is moved to the bottom.
// return value contains number1, number2, and the current position of the problem in the list
int problem, problemIndex;
if(bias < 0 || bias > 1) bias = 0;
if(bias == 0) {
problem = random.nextInt(91);
problemIndex = problem;
}
else {
float x = asin(Math.random()*bias)/asin(bias);
problemIndex = Math.floor(91*x);
problem = currentShuffle[problemIndex];
}
// now convert "problem number" into two numbers:
int first, last;
first = (int)((Math.sqrt(8*problem + 1)-1)/2);
last = problem - first * (first+1) / 2;
// and return the result:
return {first, last, problemIndex};
}
private void shuffleProblems(int[] currentShuffle, int upDown) {
// when upDown==0, return a randomly shuffled array
// when upDown < 0, (wrong answer) move element[-upDown] to zero
// when upDown > 0, (correct answer) move element[upDown] to last position
// note - if problem 0 is answered incorrectly, don't call this routine!
int ii, temp, swap;
if(upDown == 0) {
// first an ordered list:
for(ii=0;ii<91;ii++) {
currentShuffle[ii]=ii;
}
// now shuffle it:
for(ii=0;ii<91;ii++) {
temp = currentShuffle[ii];
swap = ii + random.nextInt(91-ii);
currentShuffle[ii]=currentShuffle[swap];
currentShuffle[swap]=temp;
}
return;
}
if(upDown < 0) {
temp = currentShuffle[-upDown];
for(ii = -upDown; ii>0; ii--) {
currentShuffle[ii]=currentShuffle[ii-1];
}
currentShuffle[0] = temp;
}
else {
temp = currentShuffle[upDown];
for(ii = upDown; ii<90; ii++) {
currentShuffle[ii]=currentShuffle[ii+1];
}
currentShuffle[90] = temp;
}
return;
}
// main problem posing loop:
int[] currentShuffle = new int[91];
int[] newProblem;
int keepGoing = 1;
// initial shuffle:
shuffleProblems( currentShuffle, 0); // initial shuffle
while(keepGoing) {
newProblem = chooseProblem(bias, currentShuffle);
// pose the problem, get the answer
if(wrong) {
if(newProblem > 0) shuffleProblems( currentShuffle, -newProblem[2]);
}
else shuffleProblems( currentShuffle, newProblem[2]);
// decide if you keep going...
}