在给定值附近获得随机异常

时间:2012-04-16 16:51:11

标签: c# arrays algorithm math

我想在 double 类型的已知值数组中添加一种纹波。我指出这一点,因为Random.Next / Random.NextDouble()表现不同。

我如何才能最好地完成这项任务?

假设我在数组中有20个值,
List<double> arr = new List<double>() { 40, 40, 40, 40 ..... };
20个值,平均数量为40,总计800,以便更容易。

在这个方法之后,我希望总计仍然是800,但每个单独的值都应该被修改。值应为正数,因为之后会添加total+=i

到目前为止,使用给定数量的值的百分比来解决此问题。
1.0 / 20 = 0.05, then multiplicate that with the total and the iteration number. Then subtract the result from the remainder. Finally i just return a sort by new Guid()

正如您已经看到的,这种方法只是有点可怕,而只有大约5-20个值。就我今天的情况而言,这个数组需要有500-2000个值(每个值为0.2-0.05%)。

相反,我希望有一个衍生物或类似的,以40的+ -x%作为基础的失真。或者,甚至可能更好,+ -x%对数组中的任何单个值都有效。)

[UPDATE]

我将根据对此问题的回复添加更新问题。

    Random rnd = new Random();
    List<double> ripple = new List<double>();

    int qty = bArray.Count();
    double diff = last.Value - first.Value;

    if (qty == 1)
    {
        double linearAvg = (diff / qty) / 2;
        ripple.Add(linearAvg);
    }
    else
    {
        double[] rndarr = new double[qty];

        for (int i = 0; i < qty; i++)
            rndarr[i] = rnd.NextDouble();

        double rndArrSum = rndarr.Sum();

        for (int i = 0; i < qty; i++)
            rndarr[i] /= rndArrSum; 

        for (int i = 0; i < qty; i++)
            ripple.Add(diff * rndarr[i]);
    }

    double valueOverall = first.Value;
    for (int i = (qty > 1) ? 1 : 0; i < qty; i++)
        valueOverall += ripple[i];

已允许最后生成的值不重叠。此外,列表仅包含两个值时发生异常。 qty=1可能看起来很神奇,但它指的是对象bArray在真实中的样子。无论如何,我认为整个想法都很清楚。

4 个答案:

答案 0 :(得分:5)

您可以这样做的一种方法是生成0到1之间的N个随机数(不包括)。总结一下。然后将每个数字除以总和。您现在有一个N个随机数的列表,总和为1.现在,将每个数字乘以您想要的总和,以得到将进入最终数组的数字。

如果您希望您的值为+/-某个百分比,请使用Random.Next生成某个范围内的随机数并将它们相加。然后除以总数得到总和为1的数字列表。最后一步是相同的。

答案 1 :(得分:2)

另一种方法是循环遍历数组并按百分比值扰动。完成后,计算总数的距离,并在所有数字中平均添加超额金额。这是一些示例代码:

var test = Enumerable.Repeat<double>(40, 100).ToArray();
var percent = 0.5d;

var rand = new Random();
var expectedTotal = test.Sum();
var currentTotal = 0d;
var numCount = test.Count();

for (var i = 0; i < numCount; i++)
{
    var num = test[i];
    var range = num * percent * 2;

    var newNum = num + (rand.NextDouble() - 0.5) * (range);
    currentTotal += newNum;
    test[i] = newNum;
}

var overage = (expectedTotal - currentTotal);

for (var i = 0; i < numCount; i++)
    test[i] += overage / numCount;

答案 2 :(得分:1)

以下是我的解决方案。

基本上,它会将每个值“抖动”某个指定的百分比,然后检查原始总数与“抖动”总数之间的差异。为了使最终总数与原始总数相匹配,它会为每个“抖动”值添加一个固定金额。

从数学的角度来看,我觉得这不是一个很好的解决方案,因为我认为在每个值中加入固定数量可能会扭曲每个值的实际百分比。可能有一种更加数学上正确的方法来应用整个值集中的余数,以保持预期的abberation百分比,但我想这样做需要多次传递,而这个解决方案完成了一定数量的通行证。

// prepare data
double[] values = new double[20];
for (int i = 0; i < values.Length; i++)
{
    values[i] = 40.0;
}

// get the original total
double originalTotal = 0.0;
for (int i = 0; i < values.Length; i++)
{
    originalTotal += values[i];
}

// specify an abberation percentage
double x = 0.05;

// jitter each value +/- the abberation percentage
// also capture the total of the jittered values
Random rng = new Random();
double intermediateTotal = 0.0;
for (int i = 0; i < values.Length; i++)
{
    values[i] += values[i] * (rng.NextDouble() - 0.5) * (2.0 * x);
    intermediateTotal += values[i];
}

// calculate the difference between the original total and the current total
double remainder = originalTotal - intermediateTotal;

// add a flat amount to each value to make the totals match
double offset = remainder / values.Length;
for (int i = 0; i < values.Length; i++)
{
    values[i] += offset;
}

// calculate the final total to verify that it matches the original total
double finalTotal = 0.0;
for (int i = 0; i < values.Length; i++)
{
    finalTotal += values[i];
}

答案 3 :(得分:0)

为连续数字之间的每个步骤选择一个随机数(对称为零)。然后,将其添加到第一个,并从第二个中减去它:

for(int i=1; i<length; i++) {
  dx = (rng.NextDouble() - 0.5) * scale;
  arr[i-1] += dx;
  arr[i] -= dx;
}

这应该确保数组的总和保持不变(模数浮点错误),而数组元素都被修改。