随机小数导致负数

时间:2013-05-30 00:33:26

标签: c# random

我试图获取一个货币价值(使用decimal)并将其分解为分布在14个不同标签中的随机部分,这些标签加起来是原始值。

示例:值50.00

  • 1 = 1.50
  • 2 = 3.50
  • 3 = 7.00
  • 4 = .75
  • 5 = 1.25
  • 6 = 2.00
  • 7 = 5.50
  • 8 = 6.10
  • 9 = .60
  • 10 = .50
  • 11 = 5.30
  • 12 = 3.00
  • 13 = 3.00
  • 14 = 10.00

(不是确切的值,而是像这样的随机值,总计达到总数)

问题在于我无法弄清楚以下内容:

  1. 第一个随机数很高(如果值为50,则第一个数字通常是40个),其余数字低于5个左右
  2. 其次,输出数字不是十进制格式 - 它们都是整数
  3. 最后,一半的数字是正数,一半是负数。它确实加起来我想要的数字,但我不想要负输出。
  4. 我正在使用的代码:

     private void generatewins(Decimal amount)
        {
            Decimal initialValue = amount;
            Decimal currentRemainder = amount;
            var values = new List<int>();
            Label[] labels = { win1, win5, win10, win9, win6, win2, win3, win7, win8, win11, win13, win12, win4 };
            Random rnd = new Random();
            Decimal remainingPortion = initialValue;
    
            for (int i = 0; i < 13; i++)
            {
    
                Decimal val = currentRemainder - rnd.Next(14 - i);
    
                currentRemainder -= val;
    
    
                labels[i].Text = Convert.ToString(val) + "";
                win14.Text = currentRemainder.ToString();
            }
        }
    

    为了澄清一些事情,我使用标签的原因是我必须将这些标签放在表单上的特定位置,以便它们随表单大小和表单上的另一个对象而变化。我认为使用标签会更容易。此外,第14号不属于其余部分;它被设置为从初始值中取出剩余的,因为当我将它添加到随机分割值时,它总是会变为0而其他13个值将不会总和为初始值。

3 个答案:

答案 0 :(得分:1)

好的,从问题2开始)他们是整数的原因是因为你的随机结果将是一个整数。使用rnd.NextDouble()代替,这会得到0到1之间的随机十进制数。

这可能听起来像是一个糟糕的范围,但它有利于解决问题1)结果开始大而后变小。这显然是因为随着循环的进行,您提供的随机范围会发生变化和缩小。我会做的只是循环通过14次(或者它是什么)并在0和1之间得到14个随机数。然后!将所有数字相加,将该总和除以总值(在您的情况下为50.00),然后再次检查并将所有数字乘以该结果。这将使数字加起来总数,并使所有数字都有相同的机会成为范围内的任何数据

decimal Total = 50.00;
decimal randTotal = 0;
decimal []values = new decimal[14];
for(int i = 0; i<14; i++)
{
    values[i] = rnd.NextDouble();
    randTotal += values[i]
}

decimal co = Total / randTotal;

for (int i = 0; i < 14; i++)
   values[i] *= co;

值将是一个随机十进制数组,加起来为总计。

然后如果你想整理四舍五入(到最近的分) 我会做的是以下几点:

decimal leftOver = 0;
for(int i = 0; i<14; i++)
{
   decimal centFraction = values[i] - Math.floor(values[i] * 100) / 100; //floor rounds a number down to the nearest whole number
   values[i]-=centFraction;
   leftOver+=centFraction;
}
//then just add the left over cents (which should be in whole cents!) to one of them
values[0]+=leftOver;

这可能不是最好的方法,因为从技术上来说,现在没有均匀分布,但可能会这样做?

P.S。问题3是由于您只是根据与总数无关的范围得到14个随机数这一事实。如果您的总金额为2美元,您可以清楚地看到您第一次拨打rnd.Next(14-i)(获得0到14之间的随机数)可能很容易导致数字大于2。

答案 1 :(得分:0)

我认为你需要一个类似

的循环
decimal Total = 50.00M;
for ( i = 0; i <= 14; i++)
{
    Random random = new Random();
    decimal newDec = Math.Round((random.NextDouble(0 , Total) / 100),3));
    //do something to log newDec
    Total -= newDec;
}

答案 2 :(得分:0)

Random类返回整数。如果你想要十进制数,你可以除以100。

另请注意,Next的参数是随机数的大小。您应该将其限制为所需数量的大小。这就是你得到负数的原因 - 如果返回的随机数大于你当前的余数,(只要(14-i)最终大于当前余数就会发生这种情况,那么这个值就会出现负值。

随着您的第一个数字变大,请考虑第一次迭代的可能结果。随机数介于0到14之间,因此val结果必须始终在比原始数字小14的范围内。你真正想要的是一个偏态分布,其中第一个val可以是从零到原始值的任何地方,但平均值在某处(CurrentRemainder /(14-i))。这将提供更多随机值。我需要更多的数学专家才能知道确保正确随机数的最佳分布,但是如果每次迭代都保证在零到当前余数的范围内,那么至少你不会超过总数。一个简单的解决方案是将值限制在0到平均值的两倍之间,就像这样......

Decimal currentRemainder = initialValue;

for (int i = 0; i < 13; i++)
{
    Decimal avg = (currentRemainder/(14-i));
    Decimal val = currentRemainder - (rnd.Next(avg*2*100)/100);

    currentRemainder -= val;

    labels[i].Text = Convert.ToString(val) + "";
    win14.Text = currentRemainder.ToString();
}
labels[13] = Convert.ToString(currentRemainder);

那应该非常接近。获得完全完美的随机分布将需要更多的数学来调整每个值,这有点超出我的范围。