【问题标题】:Get a randomized aberrancy around given value获得围绕给定值的随机异常
【发布时间】:2012-04-28 00:42:13
【问题描述】:

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

[更新]

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

    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 的真实外观。无论如何,我认为整个想法很清楚。

【问题讨论】:

  • 你能随机扰动前 n-1 个值,然后使最后一个值成为所需的值,使总数达到 800?
  • @mellamokb 听起来会不成比例地扭曲最后一个值。
  • @Dr.Wily'sApprentice:确实如此,我测试过 :) 另一个想法:扰动所有的值,然后在所有数字中分散它的数量。
  • 我开始考虑控制一组百分比(除以 x 数量等),但因为我开始通过火星寻找月亮的感觉而停下来...... :)
  • 使用 guid 作为随机化的关键进行排序是最糟糕的做法。 Guid 保证是唯一的,不保证是随机的。 guid 生成器完全有权生成连续的 guid,有些确实如此!如果您想按随机数排序,则生成一个随机数。为工作使用正确的工具;仅当您需要唯一标识符时才生成 guid。

标签: c# arrays algorithm math


【解决方案1】:

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

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

【讨论】:

  • 啊哈! crz的,谢谢!很明显..现在当你说出来的时候:)。它还将自动解决第二种方法。我会在几个小时内尝试一下。
  • 更新了部分解决方案,
【解决方案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;

【讨论】:

    【解决方案3】:

    以下是我的解决方案。

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

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-05
        • 2021-12-26
        • 2017-04-26
        相关资源
        最近更新 更多