将“平均值”添加到屏幕上。 .NET的Random.Next()参数来弯曲结果 [英] Adding an "average" parameter to .NET's Random.Next() to curve results

查看:79
本文介绍了将“平均值”添加到屏幕上。 .NET的Random.Next()参数来弯曲结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够向



注意:很多人都提出了一个钟形曲线,但是问题是如何能够改变

解决方案

我正在扩展产生n个随机数并取它们的平均值以获得钟形曲线效果的想法。 紧度参数控制曲线的陡度。



编辑:将一组随机点求和以获得正态分布是得到可获得exp作为峰值函数的方程。



这是一个新集合实现此逻辑的功能。 GetExp(...)函数实现WolframAlpha找到的方程。



RandomBiasedPow(...)是感兴趣的函数。它返回指定范围内的随机数,但趋向于峰值。这种趋势的强度取决于紧密度参数。

  private Random r = new Random(); 

public double RandomNormal(两倍最小值,两倍最大值,整数紧密度)
{
double total = 0.0;
for(int i = 1; i< =紧密度; i ++)
{
total + = r.NextDouble();
}
收益((总/密封性)*(最大-最小))+最小;
}

public double RandomNormalDist(double min,double max,int紧密度,double exp)
{
double total = 0.0;
for(int i = 1; i< =紧密度; i ++)
{
total + = Math.Pow(r.NextDouble(),exp);
}

收益((总/紧度)*(最大-最小))+最小;
}


public double RandomBiasedPow(双倍最小,双倍最大,int紧密度,双峰)
{
//计算偏斜正态分布,偏斜由Math.Pow(...),指定峰值在
的范围内// //注意:此峰值在范围的前20%和后20%处将产生不可靠的结果。
//要在该范围的末端达到峰值,请考虑使用其他偏差函数

double total = 0.0;
double scaled峰值=峰值/(最大值-最小值)+最小值;

如果(scaledPeak< 0.2 || scaledPeak> 0.8)
{
抛出new Exception(峰值不能在范围的底部20%或顶部20%。 );
}

double exp = GetExp(scaledPeak);

for(int i = 1; i <=紧密度; i ++)
{
//将随机数偏向另一侧,但保持在0-1
// exp参数控制将峰偏离正态分布
total + = BiasPow(r.NextDouble(),exp);
}

收益((总/紧度)*(最大-最小))+最小;
}

public double GetExp(双峰)
{
//获取BiasPow(...)产生所需峰$ b所需的指数$ b //基于经验试验,并使用WolframAlpha
返回曲线拟合至三次方程式-12.7588 * Math.Pow(peak,3)+ 27.3205 * Math.Pow(peak,2)-21.2365 * peak + 6.31735;
}

public double BiasPow(double input,double exp)
{
return Math.Pow(input,exp);
}

这里是使用RandomBiasedPow(0,100,5,peak)的直方图,图例中显示了各种峰值。我四舍五入为0到99之间的整数,将紧密度设置为5,并尝试了20到80之间的峰值。可以在应有的位置看到峰。





接下来,我尝试将密封性提高到10 ...



< img src = https://i.stack.imgur.com/1paGg.png alt =在此处输入图片描述>



分布更紧密,高峰仍然在应有的位置。太快了!


I'd like to be able to add a "average" parameter to Random.Next(Lower, Upper). This method would have min, max and average parameters. I created a method like this a while back for testing (It used lists and was horrible), so I'd like some ideas on how to write a correct implementation.

The reason for having this functionality is for many procedural/random events in my game. Say you want trees to be 10 units tall most of the time, but still can be as low as 5 or 15. A normal Random.Next(5,15) would return results all over, but this method would have more of a bell curve toward it's results. Meaning 10 would be the most common, and going out in each direction would be less common. Moving the average down to 7 for example, would make relatively small trees (or whatever this is being used on), but large ones are still possible, however uncommon.

Previous method (pseudo-code-ish)

  1. Loop from min to max
  2. Closer to average numbers are added to the list more times
  3. A random element is selected from the list, elements closer to average are added more, so they will be more likely to be chosen.

Okay, so that's like throwing a bunch of candies in a bag and picking a random one. Yeah, slow. What are your thoughts on improving this?

Illustration: (Not exactly accurate but you see the idea)

NOTE: Many people have suggested a bell curve, but the question is how to be able to change the peak of the curve to favor one side in this sense.

解决方案

I'm expanding on the idea of generating n random numbers, and taking their average to get a bell-curve effect. The "tightness" parameter controls how steep the curve is.

Edit: Summing a set of random points to get a "normal" distribution is supported by the Central Limit Theorem. Using a bias function to sway results in a particular direction is a common technique, but I'm no expert there.

To address the note at the end of your question, I'm skewing the curve by manipulating the "inner" random number. In this example, I'm raising it to the exponent you provide. Since a Random returns values less than one, raising it to any power will still never be more than one. But the average skews towards zero, as squares, cubes, etc of numbers less than one are even smaller than the base number. exp = 1 has no skew, whereas exp = 4 has a pretty significant skew.

        private Random r = new Random();        

        public double RandomDist(double min, double max, int tightness, double exp)
        {
            double total = 0.0;
            for (int i = 1; i <= tightness; i++)
            {
                total += Math.Pow(r.NextDouble(), exp);
            }

            return ((total / tightness) * (max - min)) + min;
        }

I ran trials for different values for exp, generating 100,000 integers between 0 and 99. Here's how the distributions turned out.

I'm not sure how the peak relates to the exp value, but the higher the exp, the lower the peak appears in the range.

You could also reverse the direction of the skew by changing the line in the inside of the loop to:

 total += (1 - Math.Pow(r.NextDouble(), exp));

...which would give the bias on the high side of the curve.

Edit: So, how do we know what to make "exp" in order to get the peak where we want it? That's a tricky one, and could probably be worked out analytically, but I'm a developer, not a mathematician. So, applying my trade, I ran lots of trials, gathered peak data for various values of exp, and ran the data through the cubic fit calculator at Wolfram Alpha to get an equation for exp as a function of peak.

Here's a new set of functions which implement this logic. The GetExp(...) function implements the equation found by WolframAlpha.

RandomBiasedPow(...) is the function of interest. It returns a random number in the specified ranges, but tends towards the peak. The strength of that tendency is governed by the tightness parameter.

    private Random r = new Random();

    public double RandomNormal(double min, double max, int tightness)
    {
        double total = 0.0;
        for (int i = 1; i <= tightness; i++)
        {
            total += r.NextDouble();
        }
        return ((total / tightness) * (max - min)) + min;
    }

    public double RandomNormalDist(double min, double max, int tightness, double exp)
    {
        double total = 0.0;
        for (int i = 1; i <= tightness; i++)
        {
            total += Math.Pow(r.NextDouble(), exp);
        }

        return ((total / tightness) * (max - min)) + min;
    }


    public double RandomBiasedPow(double min, double max, int tightness, double peak)
    {
        // Calculate skewed normal distribution, skewed by Math.Pow(...), specifiying where in the range the peak is
        // NOTE: This peak will yield unreliable results in the top 20% and bottom 20% of the range.
        //       To peak at extreme ends of the range, consider using a different bias function

        double total = 0.0;
        double scaledPeak = peak / (max - min) + min;

        if (scaledPeak < 0.2 || scaledPeak > 0.8)
        {
            throw new Exception("Peak cannot be in bottom 20% or top 20% of range.");
        }

        double exp = GetExp(scaledPeak);

        for (int i = 1; i <= tightness; i++)
        {
            // Bias the random number to one side or another, but keep in the range of 0 - 1
            // The exp parameter controls how far to bias the peak from normal distribution
            total += BiasPow(r.NextDouble(), exp);
        }

        return ((total / tightness) * (max - min)) + min;
    }

    public double GetExp(double peak)
    {
        // Get the exponent necessary for BiasPow(...) to result in the desired peak 
        // Based on empirical trials, and curve fit to a cubic equation, using WolframAlpha
        return -12.7588 * Math.Pow(peak, 3) + 27.3205 * Math.Pow(peak, 2) - 21.2365 * peak + 6.31735;
    }

    public double BiasPow(double input, double exp)
    {
        return Math.Pow(input, exp);
    }

Here is a histogram using RandomBiasedPow(0, 100, 5, peak), with the various values of peak shown in the legend. I rounded down to get integers between 0 and 99, set tightness to 5, and tried peak values between 20 and 80. (Things get wonky at extreme peak values, so I left that out, and put a warning in the code.) You can see the peaks right where they should be.

Next, I tried boosting Tightness to 10...

Distribution is tighter, and the peaks are still where they should be. It's pretty fast too!

这篇关于将“平均值”添加到屏幕上。 .NET的Random.Next()参数来弯曲结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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