您已经知道如何转换 0 到 91 之间的数字并将其变成一个滚动(来自您上一个问题的答案)。我建议您创建一个包含 N 个元素的数组,其中 N >> 91。用 0...90 填充前 91 个元素,并将计数器设置A
为 91。现在选择一个介于 0 和 A 之间的数字,选择相应的元素从数组中,并转换为乘法问题。如果答案错误,则将问题的编号附加到数组的末尾,并加A
一。
这将创建一个数组,其中采样频率将代表问题被错误解决的次数 - 但如果问题在下次被问到时正确解决,它不会再次降低频率。
另一种更好的解决方案,一个更接近你的解决方案(但不同)创建一个由 91 个频率组成的数组 - 每个初始设置为 1 - 并跟踪总和(最初为 91)。但是现在,当您选择一个随机数(介于 0 和 sum 之间)时,您会遍历数组,直到累积总和大于您的随机数 - bin 的数量是您选择的卷,然后您使用之前推导的公式将其转换. 如果答案错误,则增加 bin 并更新总和;如果它是正确的,你减少总和,但永远不要小于一,并更新总和。重复。
这应该可以准确地满足您的要求:给定一个包含 91 个数字(“bins”)的数组,随机选择一个 bin,使该 bin 的概率与其中的值成正比。返回 bin 的索引(可以使用您之前的方法将其转换为数字组合)。此函数以 bin(频率)数组作为第一个参数,累积和作为第二个参数调用。您查找前 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 从 1 到 91,而不是从 0 到 90)。
这是另一个想法(这并没有真正回答这个问题,但它可能更有趣):
您可以通过对均匀分布以外的其他内容进行抽样来调整选择特定问题的概率。例如,均匀采样的随机变量的平方将有利于较小的数字。这给了我们一个有趣的可能性:
首先,将您的 91 个数字随机排列
接下来,从非均匀分布中选择一个数字(一个有利于较小数字的数字)。由于这些数字是随机打乱的,因此它们实际上同样有可能被选中。但现在有诀窍了:如果问题(由选择的数字表示)被正确解决,则将问题编号移动到“堆栈的顶部”,它最不可能再次被选择。如果玩家弄错了,它会被移到堆栈的底部,在那里它最有可能再次被选中。随着时间的推移,困难的问题会移到堆栈的底部。
您可以使用以下变量创建具有不同偏斜度的随机分布
roll = (int)(91*(asin(Math.rand()*a)/asin(a)))
当您a
接近 1 时,该函数倾向于支持较小的数字,而较高数字的概率几乎为零:
我相信以下代码部分符合我的描述:
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...
}