6

我正在尝试模拟一个逼真的按键事件。出于这个原因,我使用的是 SendInput() 方法,但为了获得更好的结果,我需要指定 keyDOWN 和 KeyUP 事件之间的延迟!下面的这些数字显示了 DOWN 和 UP 事件之间经过的时间(以毫秒为单位)(这些是真实/有效的):

96 95 112 111 119 104 143 96 95 104 120 112 111 88 104 119 111 103 95 104 95 127 112 143 144 142 143 128 144 112 112 111111113111111135111411135111351C1114S11113111341C111134S1111111111111111 111 111 111 111 111 112 111 104 87 95

我们可以简化输出:

延迟 64 - 88 毫秒 -> 20% 的时间

延迟 89 - 135 毫秒 -> 60% 的时间

延迟 136 - 150 毫秒 -> 20 % 的时间

如何根据上面的概率触发事件?这是我现在正在使用的代码:

        private void button2_Click(object sender, EventArgs e)
        {
            textBox2.Focus();
            Random r = new Random();
            int rez = r.Next(0, 5); // 0,1,2,3,4 - five numbers total

            if (rez == 0) // if 20% (1/5)
            {
                Random r2 = new Random();
                textBox2.AppendText(" " + rez + " " + r2.Next(64, 88) + Environment.NewLine);
// do stuff
            }
            else if (rez == 4)//if 20% (1/5)
            {
                Random r3 = new Random();
                textBox2.AppendText(" " + rez + " " + r3.Next(89, 135) + Environment.NewLine);
// do stuff

            }
            else // if 1 or 2 or 3 (3/5) -> 60%
            {
                Random r4 = new Random();
                textBox2.AppendText(" " + rez + " " + r4.Next(136, 150) + Environment.NewLine);
// do stuff

            }

        }

这段代码有一个很大的问题。理论上,经过数百万次迭代后 - 生成的图形将类似于以下内容:

比较

如何处理这个问题?

编辑:解决方案是按照人们的建议使用分发。

这是此类代码的java实现:

http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Random.html#nextGaussian%28%29

这是 C# 实现:

如何从整数范围生成正态分布随机数?

尽管我建议稍微降低“偏差”的值。

这是有趣的 msdn 文章

http://blogs.msdn.com/b/ericlippert/archive/2012/02/21/generating-random-non-uniform-data-in-c.aspx

大家感谢帮助!

4

3 回答 3

2

这是正确的想法,我只是认为您需要使用双精度而不是整数,这样您就可以在 0 和 1 之间划分概率空间。这将使您获得更精细的粒度,如下所示:

  1. 通过将所有值除以最大值来规范化实际值
  2. 将值划分为桶 - 桶越多,图形越接近连续情况
  3. 现在,桶越大,引发事件的机会就越大。因此,根据每个桶中有多少元素来划分区间 [0,1]。所以,如果你有 20 个实数值,而一个桶里面有 5 个值,它会占用四分之一的区间。
  4. 在每次测试中,使用 Random.NextDouble() 生成一个介于 0-1 之间的随机数,并且随机数落入哪个桶中,使用该参数引发一个事件。因此,对于您提供的数字,以下是 5 个 buckets buckets 的值:

在此处输入图像描述

这在代码示例中有点多,但希望这能给出正确的想法

于 2012-04-30T10:46:13.633 回答
2

听起来您需要生成正态分布。内置的 .NET 类生成一个Uniform Distribution

通过使用Box-Muller 变换,使用内置 Random 类可以实现高斯或正态分布随机数。

你应该得到一个像这样的漂亮概率曲线

正态分布

(取自http://en.wikipedia.org/wiki/Normal_distribution

要将正态分布的随机数转换为整数范围,Box-Muller 变换可以再次帮助解决此问题。请参阅前面的问题和答案,其中描述了过程和数学证明的链接。

于 2012-04-30T10:52:19.433 回答
1

一种可能的方法是将延迟建模为指数分布。指数分布模拟以恒定平均速率连续且独立发生的事件之间的时间 - 考虑到您的问题,这听起来像是一个公平的假设。

您可以通过取实际观察到的延迟的平均值的倒数来估计参数 lambda,并使用这种方法模拟分布,即

延迟 = -Math.Log(random.NextDouble()) / lambda

但是,查看您的样本,数据看起来过于“集中”在均值周围,无法成为纯指数,因此以这种方式进行模拟会导致正确均值的延迟,但过于分散而无法匹配您的样本。

解决这个问题的一种方法是将过程建模为移位的指数;本质上,该过程移动了一个值,该值表示该值可以采用的最小值,而不是指数的 0。在代码中,将偏移作为样本中的最小观察值,可能如下所示:

var sample = new List<double>()
                  {
                     96,
                     95,
                     112,
                     111,
                     119,
                     104,
                     143,
                     96,
                     95,
                     104,
                     120,
                     112
                  };

var min = sample.Min();
sample = sample.Select(it => it - min).ToList();

var lambda = 1d / sample.Average();

var random = new Random();
var result = new List<double>();
for (var i = 0; i < 100; i++)
{
   var simulated = min - Math.Log(random.NextDouble()) / lambda;
   result.Add(simulated);
   Console.WriteLine(simulated);
}

一个简单的替代方案,本质上类似于 Aidan 的方法,是重新采样:从原始样本中选择随机元素,结果将具有完全期望的分布:

var sample = new List<double>()
                  {
                     96,
                     95,
                     112,
                     111,
                     119,
                     104,
                     143,
                     96,
                     95,
                     104,
                     120,
                     112
                  };

var random = new Random();
var size = sample.Count();
for (var i = 0; i < 100; i++)
{
   Console.WriteLine(sample[random.Next(0, size)]);
}
于 2012-05-02T03:48:40.010 回答