1

我想在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%)。

相反,我想有一个衍生物或类似的东西,它会以 +-x% 的值 40 为基础进行失真。或者,也许更好,在数组中的任何单个值上 +-x%)。

[更新]

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

    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

4 回答 4

5

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

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

于 2012-04-16T17:04:02.417 回答
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;
于 2012-04-16T17:05:40.843 回答
1

下面是我的解决方案。

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

从数学的角度来看,我觉得这不是一个很好的解决方案,因为我认为将固定量添加到每个值可能会扭曲每个值的真实偏差百分比。可能有一种在数学上更正确的方法可以在一组值中应用余数,以保持预期的畸变百分比,但我想这样做需要几次通过,而这个解决方案在一组数量的通过。

// 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];
}
于 2012-04-16T17:15:38.640 回答
0

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

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

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

于 2012-04-16T17:26:23.373 回答