【问题标题】:Random decimal leads to negative随机小数导致负数
【发布时间】:2013-05-30 00:33:26
【问题描述】:

我正在尝试获取货币值(使用 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. 最后,一半是正数,一半是负数。它确实加起来就是我想要的数字,但我不想要负输出。

我正在使用的代码:

 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 个值不会与初始值相加。

【问题讨论】:

    标签: c# random


    【解决方案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;
    

    values 将是一个随机十进制数的数组,加起来为 Total。

    那么如果你想整理四舍五入(到最接近的分) 我要做的是:

    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;
    

    这可能不是最好的方法,因为这意味着从技术上讲现在没有均匀分布,但它可能会吗?

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

    【讨论】:

    • 虽然每个人的回答似乎都提供了更好的输出,但我能想出的这个例子对我有很大帮助,这对最后几位小数来说是完美的,并且允许我将剩下的用于我以前的其他东西一旦我能把所有的东西都加起来就去做,谢谢。
    【解决方案2】:

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

    还要注意Next的参数是随机数的大小。您应该将其限制为您想要的数字的大小。这就是您得到负数的原因 - 如果返回的随机数大于您当前的余数(这可能会在 (14-i) 结束时大于当前余数时发生,那么该值将变为负数。

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

    十进制 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);
    

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

    【讨论】:

      【解决方案3】:

      我认为你需要一个类似的循环

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

      【讨论】:

      • 这就是我遇到问题的地方,我一开始几乎完全一样,但是使用 rnd.Next(0,Total) 会给我一个错误,假设因为 Total 不是 Int。如果我做 rnd.Next(0, Convert.ToInt32(Total));它可以工作,但不能完全加起来 50,而且所有输出都是整数
      • 我明白了,我马上改答案
      猜你喜欢
      • 2011-12-17
      • 2011-04-19
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2018-02-04
      相关资源
      最近更新 更多