1

我试图取一个货币值(使用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 个值不会与初始值相加。

4

3 回答 3

1

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

这可能听起来像一个糟糕的范围,但它会很好地解决问题 1)结果开始很大然后变小。这显然是因为您提供的随机范围随着循环的进行而变化和缩小。我要做的就是简单地循环 14 次(或者它是什么)并获得 14 个介于 0 和 1 之间的随机数。然后!将所有数字相加,将该总和除以总值(在您的情况下为 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;

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

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

于 2013-05-30T00:54:18.160 回答
0

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

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

于 2013-05-30T01:01:59.137 回答
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;
}
于 2013-05-30T00:41:16.807 回答