使用间距在数组中设置随机数

时间:2015-10-14 01:53:14

标签: arrays ruby random

我需要用N个随机数填充Ruby数组。但是,我需要确保它们彼此分开X个数字。

例如,我需要填充一个包含5个数字的数组,随机生成1到100之间的数字,但要确保没有数字比3更接近。

所以这是有效的:

[1, 4, 7, 22, 84]  # the differences between the numbers are >= 3

这是无效的:

[1, 2, 50, 70, 90] # "1" and "2" are too close together

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

这是一个想法:每次生成一个随机数时,从候选者中删除与其接近的每个数字:

def gen_rand_arr(arr, diff)
  sample = arr.sample
  arr.delete_if {|x| (x - sample).abs < diff }
  sample
end

然后使用它:

arr = (1..100).to_a
result = []
1.upto(5) { result << gen_rand_arr(arr, 3) }
p result.sort

答案 1 :(得分:1)

i = 1
while i < 100  do
   final = rand(i+3..i+20)
   (a ||= []).push(final)
   i +=20
end
p a

一些随机的例子。

[18, 39, 48, 65, 83]
[9, 27, 56, 66, 100]
[10, 29, 46, 68, 86]
[11, 34, 57, 64, 86]
[3, 31, 46, 70, 99]
[16, 36, 43, 75, 92]

答案 2 :(得分:0)

这个随机版本怎么样?

sample_result = []
arr = (1..100).to_a
diff = 15
result_len = 5
1.upto(result_len) {
    sample = arr.sample
    sample_result << sample
    (sample-diff..sample+diff).each do 
        |i| arr.delete(i)
    end
}
p sample_result.sort

答案 3 :(得分:0)

这是解决这个问题的另一种方法。

允许的最小数字是:

a = Array.new(5) { |i| i * 3 + 1 }
#=> [1, 4, 7, 10, 13]

我们可以将这些数字(或这组数字)增加100 - 13 = 87次,直到我们结束:

87.times { a[0..-1] = a[0..-1].map(&:next) }
a #=> [88, 91, 94, 97, 100]

这是允许的最大数字。

我们不是递增所有元素,而是每次都可以选择一个随机元素并递增一个(并且它是正确的邻居):

def spread(size, count, step)
  arr = Array.new(count) { |i| i * step + 1 }
  (size - arr.last).times do
    i = rand(0..arr.size)
    arr[i..-1] = arr[i..-1].map(&:next)
  end
  arr
end

5.times do
  p spread(100, 5, 3)
end

输出:

[21, 42, 56, 73, 86]
[6, 21, 45, 61, 81]
[20, 33, 48, 63, 81]
[12, 38, 55, 75, 90]
[11, 29, 50, 71, 86]
[26, 44, 64, 79, 95]

不幸的是,我们必须循环几次才能生成最终值。这不仅速度慢,而且导致分布不均匀:

Distribution 1

最好确定总计为87的6个随机偏移并相应地移动元素。为什么6?因为偏移量是我们5个数字之间的距离,即:

       n1       n2                 n3     n4           n5            
|<-o1->|<--o2-->|<-------o3------->|<-o4->|<----o5---->|<----o6---->|
0                                                                  max

这个辅助方法返回这样的偏移量:(从here被盗)

def offsets(size, count)
  offsets = Array.new(count) { rand(0..size) }.sort
  [0, *offsets, size].each_cons(2).map { |a, b| b - a }
end

o = offsets(87, 5) #=> [3, 0, 15, 4, 64, 1]
o.inject(:+)       #=> 87

我们可以将偏移量添加到我们的初始数字数组中:

def spread(size, count, step)
  arr = Array.new(count) { |i| i * step }
  offsets(size - arr.last, count).each_with_index do |offset, index|
    arr[index..-1] = arr[index..-1].map { |i| i + offset }
  end
  arr
end

5.times do
  p spread(99, 5, 3)
end

输出:

[1, 14, 48, 60, 94]
[12, 46, 54, 67, 72]
[8, 14, 35, 40, 45]
[27, 30, 51, 81, 94]
[63, 79, 86, 90, 96]

正如预期的那样,这会导致随机分布:

Distribution 2

看起来更好。请注意,这些结果基于零。

我们甚至可以删除初始数组并从偏移量计算最终值。从第一个偏移量开始,我们只需要添加以下每个偏移量加上3:

def spread(size, count, step)
  offs = offsets(size - (count - 1) * step, count)
  (1...offs.size).each { |i| offs[i] += offs[i-1] + step }
  offs[0, count]
end