这是解决此问题的另一种方法。
允许的最小数字是:
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]
不幸的是,我们必须循环多次才能生成最终值。这不仅速度慢,而且会导致分布不均匀:
最好确定 6 个总和为 87 的随机偏移量并相应地移动元素。为什么是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]
正如预期的那样,这会导致随机分布:
这样看起来更好。请注意,这些结果是从零开始的。
我们甚至可以删除初始数组并根据偏移量计算最终值。从第一个偏移量开始,我们只需将后面的每个偏移量加上 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