获取给定值附近的随机畸变

本文关键字:随机 畸变 获取 | 更新日期: 2023-09-27 18:31:29

我想为双精度类型的已知值数组添加一种波纹。我指出这一点是因为 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%)。

[更新]

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

    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 在现实中的样子。无论如何,我认为整个想法都很清楚。

获取给定值附近的随机畸变

一种方法

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

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

另一种方法是遍历数组并按百分比值扰动。 完成后,计算总数的差距,并将分布在所有数字中的超额金额相等。 下面是一些示例代码:

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;

以下是我的解决方案。

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

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

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

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

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

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