Java偏爱三角数组中的随机数 [英] Java Biasing Random Numbers in a Triangular Array

查看:76
本文介绍了Java偏爱三角数组中的随机数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题是

This question is an extension of Java- Math.random(): Selecting an element of a 13 by 13 triangular array. I am selecting two numbers at random (0-12 inclusive) and I wanted the values to be equal.

但是,现在,由于这是一个乘法游戏,我想要一种偏向结果的方法,以便某些组合出现的频率更高(例如,如果Player的12x8表现更差,我希望它出现的频率更高).最终,我想偏向于91种组合中的任何一种,但是一旦我掌握了这一点,那就不难了.

But now, since this is a multiplication game, I want a way to bias the results so certain combinations come up more frequently (like if the Player does worse for 12x8, I want it to come up more frequently). Eventually, I would like to bias towards any of the 91 combinations, but once I get this down, that should not be hard.

我的想法:在三角形数字和Random.nextInt(91 + n)之间添加一些int n,以使结果偏向组合.

My Thoughts: Add some int n to the triangular number and Random.nextInt(91 + n) to bias the results toward a combination.

private int[] triLessThan(int x, int[] bias) { // I'm thinking a 91 element array, 0 for no bias, positive for bias towards
    int i = 0;
    int last = 0;
    while (true) {
                    int sum = 0;
                    for (int a = 0; a < i * (i + 2)/2; a++){
                        sum += bias[a]
                    }
        int triangle = i * (i + 1) / 2;
        if (triangle + sum > x){
            int[] toReturn = {last,i};
            return toReturn;
        }
        last = triangle;
        i++;
    }
}

在随机数栏中:

int sum = sumOfArray(bias); // bias is the array;
int roll = random.nextInt(91 + sum);
int[] triNum = triLessThan(roll);
int num1 = triNum[1];
int num2 = roll - triNum[0]; //now split into parts and make bias[] add chances to one number.

其中sumOfArray只是找到总和(该公式很简单).这样行吗?

where sumOfArray just finds the sum (that formula is easy). Will this work?

使用弗洛里斯的想法:

Using Floris's idea:

随机数滚动:

int[] bias = {1,1,1,...,1,1,1} // 91 elements
int roll = random.nextInt(sumOfBias());
int num1 = roll;
int num2 = 0;
while (roll > 0){
    roll -= bias[num2];
    num2++;
}
num1 = (int) (Math.sqrt(8 * num2 + 1) - 1)/2;
num2 -= num1 * (num1 + 1) / 2;

推荐答案

您已经知道如何将0到91之间的数字转换为小数点(从上一个问题的答案中得出).我建议您创建一个由N个元素组成的数组,其中N >>91.用0 ... 90填充前91个元素,并将计数器A设置为91.现在选择一个介于0和A之间的数字,选择数组中的相应元素,然后转换为乘法问题.如果答案不正确,则将问题的编号附加到数组的末尾,然后将A加1.

You already know how to convert a number between 0 and 91 and turn it into a roll (from the answer to your previous question). I would suggest that you create an array of N elements, where N >> 91. Fill the first 91 elements with 0...90, and set a counter A to 91. Now choose a number between 0 and A, pick the corresponding element from the array, and convert to a multiplication problem. If the answer is wrong, append the number of the problem to the end of the array, and increment A by one.

这将创建一个数组,其中采样频率将代表错误解决问题的次数-但是,如果下次再次提出问题可以正确解决问题,它将永远不会再次降低频率.

This will create an array in which the frequencies of sampling will represent the number of times a problem was solved incorrectly - but it doesn't ever lower the frequency again if the problem is solved correctly the next time it is asked.

另一种更好的解决方案是,它与您的解决方案有点接近(但截然不同),它创建了一个由91个频率组成的阵列-每个初始设置为1-并跟踪总和(最初为91个).但是现在,当您选择一个随机数(介于0和总和之间)时,您将遍历数组直到累积总和大于您的随机数-箱的数量即为您选择的掷骰,然后使用之前导出的公式对其进行转换.如果答案是错误的,则增加垃圾箱并更新总和;如果是正确的,则将总和递减,但绝对不要小于一,然后更新总和.重复.

An alternative and better solution, and one that is a little closer to yours (but distinct) creates an array of 91 frequencies - each initially set to 1 - and keeps track of the sum (initially 91). But now, when you choose a random number (between 0 and sum) you traverse the array until the cumulative sum is greater then your random number - the number of the bin is the roll you choose, and you convert that with the formula derived earlier. If the answer is wrong you increment the bin and update the sum; if it is right, you decrement the sum but never to a value less than one, and update the sum. Repeat.

这应该完全满足您的要求:给定一个由91个数字组成的数组("bins"),以一种使该bin的概率与其中的值成正比的方式随机选择一个bin.返回垃圾箱的索引(可以使用以前的方法将其转换为数字的组合).以bin(频率)数组作为第一个参数,以累积和作为第二个参数来调用此函数.您查找前n个元素的累加总和首先超过按频率总和缩放的随机数的位置:

This should give you exactly what you are asking: given an array of 91 numbers ("bins"), randomly select a bin in such a way that the probability of that bin is proportional to the value in it. Return the index of the bin (which can be turned into the combination of numbers using the method you had before). This function is called with the bin (frequency) array as the first parameter, and the cumulative sum as the second. You look up where the cumulative sum of the first n elements first exceeds a random number scaled by the sum of the frequencies:

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;
}

我确认它给了我一个直方图(蓝色条形),看起来与我喂给它的概率分布(红线)完全一样:

I confirmed that it gives me a histogram (blue bars) that looks exactly like the probability distribution that I fed it (red line):

(请注意-这是使用matlab绘制的,因此X从1到91,而不是从0到90).

(note - this was plotted with matlab so X goes from 1 to 91, not from 0 to 90).

这是另一个想法(虽然并没有真正回答问题,但是可能更有趣):

Here is another idea (this is not really answering the question, but it's potentially even more interesting):

您可以通过采样均匀分布以外的内容来偏斜选择特定问题的概率.例如,统一采样的随机变量的平方将倾向于较小的数字.这给了我们一个有趣的可能性:

You can skew your probability of choosing a particular problem by sampling something other than a uniform distribution. For example, the square of a uniformly sampled random variate will favor smaller numbers. This gives us an interesting possibility:

首先,将您的91个数字随机排列

First, shuffle your 91 numbers into a random order

接下来,从非均匀分布中选择一个数字(一个偏爱较小数字的数字).由于数字是随机排列的,因此实际上有可能被选择.但是,这就是窍门:如果问题(由所选择的数字表示)正确解决了,则将问题编号移至堆栈顶部",在该位置最不可能再次选择该问题编号.如果玩家弄错了,它将被移到堆栈的底部,最有可能再次被选择.随着时间的流逝,困难的问题逐渐移到堆栈的底部.

Next, pick a number from a non-uniform distribution (one that favors smaller numbers). Since the numbers were randomly shuffled, they are in fact equally likely to be chosen. But now here's the trick: if the problem (represented by the number picked) is solved correctly, you move the problem number "to the top of the stack", where it is least likely to be chosen again. If the player gets it wrong, it is moved to the bottom of the stack, where it is most likely to be chosen again. Over time, difficult problems move to the bottom of the stack.

您可以使用

roll = (int)(91*(asin(Math.rand()*a)/asin(a)))

使a接近1时,函数倾向于偏低的数字,偏高的数字几乎为零:

As you make a closer to 1, the function tends to favor lower numbers with almost zero probability of higher numbers:

我相信以下代码段可以实现我所描述的功能:

I believe the following code sections do what I described:

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...
}

这篇关于Java偏爱三角数组中的随机数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆