如何生成平均值遵循 Ruby 正弦波的随机数?

How can I generate random numbers whose average follows a sine wave in Ruby?

我不是学数学的,所以我真的不知道我想做的事情叫什么名字,但我确定它有一个名字。 ;-)

我想在 Ruby 中生成随机数数组,数组中每个元素的平均值都遵循正弦波。我所说的 每个元素的平均值 是元素 n 的平均值 ary[0..n].inject(:+).to_f / (n + 1)。因此,如果我从 0..n 遍历随机数数组并像我描述的那样生成平均值,我希望结果值遵循正弦波。我只是不知道如何以这种方式实际生成随机数...

# assuming `ary` is the array of random numbers
# I'm trying to figure out how to generate...

averages = []

(0..ary.size).each do |n|
  averages << ary[0..n].inject(:+).to_f / (n + 1)
end

# `averages` should plot as a sine wave now...

这是一个主意。创建一个 class,它有一些样本大小,它在正弦波中生成点,加上一些高于或低于该点的随机 "fudge factor"(方差)。这样,如果您绘制样本量中的点数,您应该根据配置的方差(软糖因子)看到带有 "roughness" 的正弦波。

class RandomSineWave
  attr_reader :size
  def initialize(size=20, variance=0.2)
    @size = size
    @step = 2 * Math::PI / size
    @position = 0
    @variance = variance
  end
  def next
    @position = 0 if @position >= 2 * Math::PI
    next_rand = Math.sin(@position) + (rand * @variance) - (@variance / 2)
    @position += @step
    next_rand
  end
end

# Generate TSV output for demonstration.
rsw = RandomSineWave.new
rsw.size.times { |i| puts [i, rsw.next].join "\t" }

您可以通过修改构造函数的第二个参数来 fiddle 和 "roughness":

rsw = RandomSineWave.new(20, 0.8) # Results plotted below...

据我了解,给定一些正整数 n,您想要构建一个 n 概率分布数组,使得随机变量的部分和的期望值描述一个正弦波。我假设正弦波在区间 (0..2*π) 内并且期望值在该区间内均匀分布。

我们必须首先问这些概率分布是否在统计上是独立的。如果它们不是,它就会变得非常复杂,所以我会假设它们是独立的。在针对它们的均值差异进行调整后,它们是否是相同的分布是没有必要的,甚至是不重要的。我稍后再讲。

由于您希望随机变量 Xi 的部分和的期望值来描述正弦波,我们要求:

E [∑j=0...iXj] = k * sin(2*π*i/n)

对于所有 i = 0...n-1,对于给定的比例因子,k(其中(E[..] 表示 "expected value")。我们可以假设,不失一般性, k=1,因为我们总是可以按 k 缩放随机变量,导致它们的均值按相同的常数缩放。

因为分布是独立的,我们可以写成:

j=0...imj = sin(2*π*i/n)

哪里

mi = E[Xi] 是 Xi 的均值。

在 Ruby 中,对于 n 值(浮点数)的数组 x,这是:

x[0,i].reduce(:+) = Math::sin(2.0 * Math::PI * i.to_f/n)

我们可以很容易地计算出x。假设 n = 36。 对于 i = 0:

x[0,0].reduce(:+) = Math::sin(2.0 * Math::PI * 0.0/36)
  # x[0] = 0

设:

s = x[0]
  #=> 0.0

对于i = 1

x[0,1].reduce(:+) = Math::sin(2.0 * Math::PI * 1.0/36).round(6)
  #=>    0.0 + x[1] = Math::sin(0.17453292519943295).round(6) 
  #=>               = 0.173648

所以

x[1] = 0.173648 - 0.0
  #=> 0.173648

现在让

s += x[1]
  #=> 0.173648

对于i = 2:

x[0,2].reduce(:+)     = Math::sin(2.0 * Math::PI * 2.0/36).round(6)
  #=>        s + x[2] = Math::sin(0.3490658503988659).round(6) 
  #=> 0.173648 + x[2] = 0.342020 

所以

x[2] = 0.342020 - 0.173648
  #=> 0.168372

然后我们更新s:

s += 0.168372
  #=> 0.173648 += 0.168372  
  #=> 0.342020

然后类似地计算x[3],然后每个剩余的x

def compute(n, p=6)
  sum = 0.0
  n.times.map do |i|
    if i.zero?
      [0.0, 0.0, 0.0, 0.0]
    else
      x = Math::sin(2.0 * Math::PI * i.to_f/n) - sum
      sum += x
      [(2.0*(i.to_f/n)*Math::PI).round(p), x.round(p),
        sum.round(p), Math::sin(sum).round(p)]
    end
  end
end

compute(36)
  #   radians    x          sum        sin(sum)    degrees
  # [[0.0,       0.0,       0.0,       0.0     ],     0
  #  [0.174533,  0.173648,  0.173648,  0.172777],
  #  ...
  #  [1.396263,  0.045115,  0.984808,  0.833166],
  #  [1.570796,  0.015192,  1.0,       0.841471],    90
  #  [1.745329, -0.015192,  0.984808,  0.833166],
  #  ...
  #  [2.967060, -0.168372,  0.173648,  0.172777],
  #  [3.141593, -0.173648,  0.0,       0.0     ],   180
  #  [3.316126, -0.173648, -0.173648, -0.172777],
  #  ...
  #  [4.537856, -0.045115, -0.984808, -0.833166],
  #  [4.712389, -0.015192, -1.0,      -0.841471],   270
  #  [4.886922,  0.015192, -0.984808, -0.833166],  
  #  ...
  #  [5.934119,  0.15798,  -0.34202,  -0.335391],
  #  [6.108652,  0.168372, -0.173648, -0.172777]]   350 

当我有时间熟悉@maeric 的漂亮绘图工具时,我将添加这些值的图表。

现在我们有了均值,我们可以考虑构建具有这些均值的概率分布。

假设,例如,我们假设每个随机变量具有相同的均匀分布,范围(最大-最小)为rng,均值不同。如果平均值是 0.325467,我们可以生成一个伪随机变量,如下所示:

rng * (rand-0.325467)

哪里

(0.5-0.174533).round(6)
  #=> 0.325467

因此,我们可以为具有给定范围的均匀分布生成伪随机变量,均值如下:

def uniform_rv(rng, mean)
  rng.to_f * (rand -0.5 -mean)
end